[dev.ssa] Merge branch 'master' into dev.ssa

Change-Id: Iabc80b6e0734efbd234d998271e110d2eaad41dd
This commit is contained in:
David Chase 2016-05-27 15:18:49 -04:00
commit 31e13c83c2
507 changed files with 31557 additions and 5826 deletions

1
.gitignore vendored
View file

@ -26,6 +26,7 @@ misc/cgo/stdio/run.out
misc/cgo/testso/main
src/cmd/cgo/zdefaultcc.go
src/cmd/go/zdefaultcc.go
src/cmd/go/zosarch.go
src/cmd/internal/obj/zbootstrap.go
src/go/build/zcgo.go
src/go/doc/headscan

90
AUTHORS
View file

@ -14,13 +14,17 @@ A Medium Corporation
Aamir Khan <syst3m.w0rm@gmail.com>
Aaron France <aaron.l.france@gmail.com>
Aaron Torres <tcboox@gmail.com>
Abe Haskins <abeisgreat@abeisgreat.com>
Abhinav Gupta <abhinav.g90@gmail.com>
Adrian Nos <nos.adrian@gmail.com>
Adrian O'Grady <elpollouk@gmail.com>
Adrien Bustany <adrien-xx-google@bustany.org>
Aécio Júnior <aeciodantasjunior@gmail.com>
Ahmed Waheed Moanes <oneofone@gmail.com>
Ahmy Yulrizka <yulrizka@gmail.com>
Aiden Scandella <ai@uber.com>
Ainar Garipov <gugl.zadolbal@gmail.com>
Akihiro Suda <suda.kyoto@gmail.com>
Akshat Kumar <seed@mail.nanosouffle.net>
Alan Shreve <alan@inconshreveable.com>
Albert Strasheim <fullung@gmail.com>
@ -28,6 +32,7 @@ Alberto Bertogli <albertito@blitiri.com.ar>
Alberto Donizetti <alb.donizetti@gmail.com>
Alberto García Hierro <alberto@garciahierro.com> <alberto.garcia.hierro@gmail.com>
Aleksandar Dezelin <dezelin@gmail.com>
Alessandro Arzilli <alessandro.arzilli@gmail.com>
Alex A Skinner <alex@lx.lc>
Alex Brainman <alex.brainman@gmail.com>
Alex Jin <toalexjin@gmail.com>
@ -50,8 +55,10 @@ Alexey Borzenkov <snaury@gmail.com>
Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
Aliaksandr Valialkin <valyala@gmail.com>
Alif Rachmawadi <subosito@gmail.com>
Amazon.com, Inc
Amir Mohammad Saied <amir@gluegadget.com>
Amrut Joshi <amrut.joshi@gmail.com>
Andre Nathan <andrenth@gmail.com>
Andrei Korzhevskii <a.korzhevskiy@gmail.com>
Andrei Vieru <euvieru@gmail.com>
Andrew Balholm <andybalholm@gmail.com>
@ -85,6 +92,8 @@ Anthony Starks <ajstarks@gmail.com>
Apisak Darakananda <pongad@gmail.com>
Aram Hăvărneanu <aram@mgk.ro>
Areski Belaid <areski@gmail.com>
Arlo Breault <arlolra@gmail.com>
ARM Ltd.
Arnaud Ysmal <arnaud.ysmal@gmail.com>
Arne Hormann <arnehormann@gmail.com>
Arnout Engelen <arnout@bzzt.net>
@ -92,6 +101,8 @@ Aron Nopanen <aron.nopanen@gmail.com>
Artyom Pervukhin <artyom.pervukhin@gmail.com>
Arvindh Rajesh Tamilmani <art@a-30.net>
Ato Araki <ato.araki@gmail.com>
Audrey Lim <audreylh@gmail.com>
Augusto Roman <aroman@gmail.com>
Aulus Egnatius Varialus <varialus@gmail.com>
awaw fumin <awawfumin@gmail.com>
Aymerick Jéhanne <aymerick@jehanne.org>
@ -107,6 +118,8 @@ Bjorn Tipling <bjorn.tipling@gmail.com>
Blake Gentry <blakesgentry@gmail.com>
Blake Mizerany <blake.mizerany@gmail.com>
Bobby Powers <bobbypowers@gmail.com>
Brady Catherman <brady@gmail.com>
Brady Sullivan <brady@bsull.com>
Brendan Daniel Tracey <tracey.brendan@gmail.com>
Brett Cannon <bcannon@gmail.com>
Brian Dellisanti <briandellisanti@gmail.com>
@ -140,17 +153,21 @@ Christoffer Buchholz <christoffer.buchholz@gmail.com>
Christoph Hack <christoph@tux21b.org>
Christopher Cahoon <chris.cahoon@gmail.com>
Christopher Guiney <chris@guiney.net>
Christopher Nelson <nadiasvertex@gmail.com>
Christopher Nielsen <m4dh4tt3r@gmail.com>
Christopher Redden <christopher.redden@gmail.com>
Christopher Wedgwood <cw@f00f.org>
CL Sung <clsung@gmail.com> <cl_sung@htc.com>
Clement Skau <clementskau@gmail.com>
CloudFlare Inc.
Colin Edwards <colin@recursivepenguin.com>
Colin Kennedy <moshen.colin@gmail.com>
Conrad Irwin <conrad.irwin@gmail.com>
Conrad Meyer <cemeyer@cs.washington.edu>
CoreOS, Inc.
Corey Thomasson <cthom.lists@gmail.com>
Cristian Staretu <unclejacksons@gmail.com>
Currant
Damian Gryski <dgryski@gmail.com>
Dan Caddigan <goldcaddy77@gmail.com>
Dan Callahan <dan.callahan@gmail.com>
@ -164,9 +181,12 @@ Daniel Lidén <daniel.liden.87@gmail.com>
Daniel Morsing <daniel.morsing@gmail.com>
Daniel Ortiz Pereira da Silva <daniel.particular@gmail.com>
Daniel Skinner <daniel@dasa.cc>
Daniel Speichert <daniel@speichert.pl>
Daniel Theophanes <kardianos@gmail.com>
Darren Elwood <darren@textnode.com>
Datong Sun <dndx@idndx.com>
Dave Cheney <dave@cheney.net>
David Brophy <dave@brophy.uk>
David Bürgin <676c7473@gmail.com>
David Calavera <david.calavera@gmail.com>
David du Colombier <0intro@gmail.com>
@ -176,21 +196,26 @@ David Howden <dhowden@gmail.com>
David Jakob Fritz <david.jakob.fritz@gmail.com>
David Leon Gil <coruus@gmail.com>
David R. Jenni <david.r.jenni@gmail.com>
David Sansome <me@davidsansome.com>
David Thomas <davidthomas426@gmail.com>
David Titarenco <david.titarenco@gmail.com>
Davies Liu <davies.liu@gmail.com>
Dean Prichard <dean.prichard@gmail.com>
Denis Bernard <db047h@gmail.com>
Denis Brandolini <denis.brandolini@gmail.com>
Denys Honsiorovskyi <honsiorovskyi@gmail.com>
Derek Buitenhuis <derek.buitenhuis@gmail.com>
Derek Parker <parkerderek86@gmail.com>
Derek Shockey <derek.shockey@gmail.com>
Develer SRL
Devon H. O'Dell <devon.odell@gmail.com>
Dhiru Kholia <dhiru.kholia@gmail.com>
Didier Spezia <didier.06@gmail.com>
Dimitri Tcaciuc <dtcaciuc@gmail.com>
Dirk Gadsden <dirk@esherido.com>
Diwaker Gupta <diwakergupta@gmail.com>
Dmitri Shuralyov <shurcooL@gmail.com>
Dmitriy Dudkin <dudkin.dmitriy@gmail.com>
Dmitriy Shelenin <deemok@googlemail.com> <deemok@gmail.com>
Dmitry Chestnykh <dchest@gmail.com>
Dmitry Savintsev <dsavints@gmail.com>
@ -200,6 +225,7 @@ Donald Huang <don.hcd@gmail.com>
Donovan Hide <donovanhide@gmail.com>
Dropbox, Inc.
Duncan Holm <mail@frou.org>
Dustin Herbison <djherbis@gmail.com>
Dustin Sallings <dsallings@gmail.com>
Dustin Shields-Cloues <dcloues@gmail.com>
Dvir Volk <dvir@everything.me> <dvirsky@gmail.com>
@ -220,6 +246,7 @@ Erik Aigner <aigner.erik@gmail.com>
Erik Dubbelboer <erik@dubbelboer.com>
Erik St. Martin <alakriti@gmail.com>
Erik Westrup <erik.westrup@gmail.com>
Ernest Chiang <ernest_chiang@htc.com>
Esko Luontola <esko.luontola@gmail.com>
Evan Phoenix <evan@phx.io>
Evan Shaw <chickencha@gmail.com>
@ -241,6 +268,7 @@ Francisco Souza <franciscossouza@gmail.com>
Frederick Kelly Mayle III <frederickmayle@gmail.com>
Fredrik Enestad <fredrik.enestad@soundtrackyourbrand.com>
Frithjof Schulze <schulze@math.uni-hannover.de> <sfrithjof@gmail.com>
Frits van Bommel <fvbommel@gmail.com>
Gabriel Aszalos <gabriel.aszalos@gmail.com>
Gary Burd <gary@beagledreams.com>
Gaurish Sharma <contact@gaurishsharma.com>
@ -266,30 +294,37 @@ Hajime Hoshi <hajimehoshi@gmail.com>
Hari haran <hariharan.uno@gmail.com>
Hariharan Srinath <srinathh@gmail.com>
Harley Laue <losinggeneration@gmail.com>
Harshavardhana <hrshvardhana@gmail.com>
Håvard Haugen <havard.haugen@gmail.com>
Hector Chu <hectorchu@gmail.com>
Hector Martin Cantero <hector@marcansoft.com>
Henning Schmiedehausen <henning@schmiedehausen.org>
Henrik Edwards <henrik.edwards@gmail.com>
Herbert Georg Fischer <herbert.fischer@gmail.com>
Hironao OTSUBO <motemen@gmail.com>
Hiroshi Ioka <hirochachacha@gmail.com>
Hitoshi Mitake <mitake.hitoshi@gmail.com>
Holden Huang <ttyh061@gmail.com>
Hong Ruiqi <hongruiqi@gmail.com>
Hsin-Ho Yeh <yhh92u@gmail.com>
Hu Keping <hukeping@huawei.com>
Ian Gudger <ian@loosescre.ws>
IBM
Icarus Sparry <golang@icarus.freeuk.com>
Idora Shinatose <idora.shinatose@gmail.com>
Igneous Systems, Inc.
Igor Dolzhikov <bluesriverz@gmail.com>
INADA Naoki <songofacandy@gmail.com>
Ingo Krabbe <ikrabbe.ask@gmail.com>
Ingo Oeser <nightlyone@googlemail.com>
Intel Corporation
Irieda Noboru <irieda@gmail.com>
Isaac Wagner <ibw@isaacwagner.me>
Ivan Ukhov <ivan.ukhov@gmail.com>
Jae Kwon <jae@tendermint.com>
Jakob Borg <jakob@nym.se>
Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com>
James Bardin <j.bardin@gmail.com>
James David Chalfant <james.chalfant@gmail.com>
James Fysh <james.fysh@gmail.com>
James Gray <james@james4k.com>
@ -299,6 +334,7 @@ James Schofield <james@shoeboxapp.com>
James Sweet <james.sweet88@googlemail.com>
James Toy <nil@opensesame.st>
James Whitehead <jnwhiteh@gmail.com>
Jamil Djadala <djadala@gmail.com>
Jan H. Hosang <jan.hosang@gmail.com>
Jan Mercl <0xjnml@gmail.com>
Jan Mercl <befelemepeseveze@gmail.com>
@ -315,6 +351,7 @@ Jeff Sickel <jas@corpus-callosum.com>
Jeff Wendling <jeff@spacemonkey.com>
Jens Frederich <jfrederich@gmail.com>
Jeremy Jackins <jeremyjackins@gmail.com>
Jess Frazelle <me@jessfraz.com>
Jihyun Yu <yjh0502@gmail.com>
Jim McGrath <jimmc2@gmail.com>
Jimmy Zelinskie <jimmyzelinskie@gmail.com>
@ -323,16 +360,21 @@ Jingguo Yao <yaojingguo@gmail.com>
Jiong Du <londevil@gmail.com>
Joakim Sernbrant <serbaut@gmail.com>
Joe Harrison <joehazzers@gmail.com>
Joe Henke <joed.henke@gmail.com>
Joe Poirier <jdpoirier@gmail.com>
Joe Shaw <joe@joeshaw.org>
Joe Sylve <joe.sylve@gmail.com>
Joe Tsai <joetsai@digital-static.net>
Joel Stemmer <stemmertech@gmail.com>
Johan Sageryd <j@1616.se>
John Asmuth <jasmuth@gmail.com>
John C Barstow <jbowtie@amathaine.com>
John Graham-Cumming <jgc@jgc.org> <jgrahamc@gmail.com>
John Howard Palevich <jack.palevich@gmail.com>
John Jeffery <jjeffery@sp.com.au>
John Jenkins <twodopeshaggy@gmail.com>
John Potocny <johnp@vividcortex.com>
John Schnake <schnake.john@gmail.com>
John Shahid <jvshahid@gmail.com>
John Tuley <john@tuley.org>
Jonathan Boulle <jonathanboulle@gmail.com>
@ -366,8 +408,13 @@ Kelvin Foo Chuan Lyi <vmirage@gmail.com>
Ken Friedenbach <kenliz@cruzio.com>
Ken Rockot <ken@oz.gs>
Ken Sedgwick <ken@bonsai.com>
Kenji Kaneda <kenji.kaneda@gmail.com>
Kenneth Shaw <kenshaw@gmail.com>
Kenny Grant <kennygrant@gmail.com>
Kevin Ballard <kevin@sb.org>
Kevin Burke <kev@inburke.com>
Kevin Kirsche <kev.kirsche@gmail.com>
Kevin Vu <kevin.m.vu@gmail.com>
Klaus Post <klauspost@gmail.com>
Konstantin Shaposhnikov <k.shaposhnikov@gmail.com>
KPCompass, Inc.
@ -379,12 +426,14 @@ Kyle Lemons <kyle@kylelemons.net>
L Campbell <unpantsu@gmail.com>
Lai Jiangshan <eag0628@gmail.com>
Larz Conwell <larzconwell@gmail.com>
Lee Hinman <hinman@gmail.com>
Lee Packham <lpackham@gmail.com>
Lewin Bormann <lewin.bormann@gmail.com>
Liberty Fund Inc
Linaro Limited
Lloyd Dewolf <foolswisdom@gmail.com>
Lorenzo Stoakes <lstoakes@gmail.com>
Luan Santos <cfcluan@gmail.com>
Luca Greco <luca.greco@alcacoop.it>
Lucien Stuker <lucien.stuker@gmail.com>
Lucio De Re <lucio.dere@gmail.com>
@ -397,6 +446,7 @@ Manuel Mendez <mmendez534@gmail.com>
Marc Weistroff <marc@weistroff.net>
Marco Hennings <marco.hennings@freiheit.com>
Mark Bucciarelli <mkbucc@gmail.com>
Mark Severson <miquella@gmail.com>
Mark Theunissen <mark.theunissen@gmail.com>
Marko Juhani Silokunnas <marko.silokunnas@gmail.com>
Marko Tiikkaja <marko@joh.to>
@ -404,12 +454,14 @@ Markover Inc. DBA Poptip
Markus Duft <markus.duft@salomon.at>
Markus Sonderegger <marraison@gmail.com>
Markus Zimmermann <zimmski@gmail.com>
Martin Garton <garton@gmail.com>
Martin Möhrmann <martisch@uos.de>
Martin Neubauer <m.ne@gmx.net>
Martin Olsson <martin@minimum.se>
Marvin Stenger <marvin.stenger94@gmail.com>
Mateusz Czapliński <czapkofan@gmail.com>
Mathias Beke <git@denbeke.be>
Mathias Leppich <mleppich@muhqu.de>
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
Mats Lidell <mats.lidell@cag.se>
Matt Aimonetti <mattaimonetti@gmail.com>
@ -419,6 +471,7 @@ Matt Jibson <matt.jibson@gmail.com>
Matt Joiner <anacrolix@gmail.com>
Matt Layher <mdlayher@gmail.com>
Matt Reiferson <mreiferson@gmail.com>
Matt Robenolt <matt@ydekproductions.com>
Matt T. Proud <matt.proud@gmail.com>
Matt Williams <gh@mattyw.net>
Matthew Brennan <matty.brennan@gmail.com>
@ -426,6 +479,7 @@ Matthew Cottingham <mattcottingham@gmail.com>
Matthew Holt <Matthew.Holt+git@gmail.com>
Matthew Horsnell <matthew.horsnell@gmail.com>
Maxim Khitrov <max@mxcrypt.com>
Maxwell Krohn <themax@gmail.com>
Meir Fischer <meirfischer@gmail.com>
Meng Zhuo <mengzhuo1203@gmail.com>
Meteor Development Group
@ -439,6 +493,7 @@ Michael Hoisie <hoisie@gmail.com>
Michael Käufl <golang@c.michael-kaeufl.de>
Michael Lewis <mikelikespie@gmail.com>
Michael MacInnis <Michael.P.MacInnis@gmail.com>
Michael McConville <momcconville@gmail.com>
Michael Pearson <mipearson@gmail.com>
Michael Schaller <michael@5challer.de>
Michael Stapelberg <michael@stapelberg.de>
@ -451,15 +506,19 @@ Mihai Borobocea <MihaiBorobocea@gmail.com>
Mikael Tillenius <mikti42@gmail.com>
Mike Andrews <mra@xoba.com>
Mike Rosset <mike.rosset@gmail.com>
Mikhail Gusarov <dottedmag@dottedmag.net>
Mikhail Panchenko <m@mihasya.com>
Miki Tebeka <miki.tebeka@gmail.com>
Mikio Hara <mikioh.mikioh@gmail.com>
Mikkel Krautz <mikkel@krautz.dk>
Miquel Sabaté Solà <mikisabate@gmail.com>
Mohit Agarwal <mohit@sdf.org>
Monty Taylor <mordred@inaugust.com>
Moov Corporation
Moriyoshi Koizumi <mozo@mozo.jp>
Morten Siebuhr <sbhr@sbhr.dk>
Môshe van der Sterre <moshevds@gmail.com>
Muhammed Uluyol <uluyol0@gmail.com>
Nan Deng <monnand@gmail.com>
Nathan John Youngman <nj@nathany.com>
Nathan Otterness <otternes@cs.unc.edu>
@ -467,17 +526,23 @@ Nathan P Finch <nate.finch@gmail.com>
Nathan VanBenschoten <nvanbenschoten@gmail.com>
Nathan Youngman <git@nathany.com>
Neelesh Chandola <neelesh.c98@gmail.com>
Netflix, Inc.
Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
ngmoco, LLC
Niall Sheridan <nsheridan@gmail.com>
Nicholas Katsaros <nick@nickkatsaros.com>
Nicholas Presta <nick@nickpresta.ca> <nick1presta@gmail.com>
Nicholas Sullivan <nicholas.sullivan@gmail.com>
Nicholas Waples <nwaples@gmail.com>
Nick Craig-Wood <nick@craig-wood.com> <nickcw@gmail.com>
Nick Patavalis <nick.patavalis@gmail.com>
Nick Petroni <npetroni@cs.umd.edu>
Nicolas Kaiser <nikai@nikai.net>
Nicolas Owens <mischief@offblast.org>
Nicolas S. Dade <nic.dade@gmail.com>
Niels Widger <niels.widger@gmail.com>
Nigel Kerr <nigel.kerr@gmail.com>
Niko Dziemba <niko@dziemba.com>
Nikolay Turpitko <nikolay@turpitko.com>
Noah Campbell <noahcampbell@gmail.com>
Norberto Lopes <nlopes.ml@gmail.com>
@ -486,8 +551,11 @@ Oling Cat <olingcat@gmail.com>
Oliver Hookins <ohookins@gmail.com>
Olivier Antoine <olivier.antoine@gmail.com>
Olivier Duperray <duperray.olivier@gmail.com>
Olivier Poitrey <rs@dailymotion.com>
Olivier Saingre <osaingre@gmail.com>
Oracle
Orange
Özgür Kesim <oec-go@kesim.org>
Padraig Kitterick <padraigkitterick@gmail.com>
Palm Stone Games
Paolo Giarrusso <p.giarrusso@gmail.com>
@ -523,10 +591,13 @@ Péter Szilágyi <peterke@gmail.com>
Peter Waldschmidt <peter@waldschmidt.com>
Peter Waller <peter.waller@gmail.com>
Peter Williams <pwil3058@gmail.com>
Philip Hofer <phofer@umich.edu>
Philip K. Warren <pkwarren@gmail.com>
Pierre Durand <pierredurand@gmail.com>
Pierre Roullon <pierre.roullon@gmail.com>
Pieter Droogendijk <pieter@binky.org.uk>
Pietro Gagliardi <pietro10@mac.com>
Prashant Varanasi <prashant@prashantv.com>
Preetam Jinka <pj@preet.am>
Quan Yong Zhai <qyzhai@gmail.com>
Quentin Perez <qperez@ocs.online.net>
@ -538,9 +609,11 @@ Ralph Corderoy <ralph@inputplus.co.uk>
Red Hat, Inc.
Reinaldo de Souza Jr <juniorz@gmail.com>
Rémy Oudompheng <oudomphe@phare.normalesup.org>
Ricardo Padilha <ricardospadilha@gmail.com>
Richard Barnes <rlb@ipv.sx>
Richard Crowley <r@rcrowley.org>
Richard Eric Gavaletz <gavaletz@gmail.com>
Richard Miller <miller.research@gmail.com>
Richard Musiol <mail@richard-musiol.de>
Rick Arnold <rickarnoldjr@gmail.com>
Risto Jaakko Saarelma <rsaarelm@gmail.com>
@ -556,6 +629,7 @@ Rodrigo Moraes de Oliveira <rodrigo.moraes@gmail.com>
Rodrigo Rafael Monti Kochenburger <divoxx@gmail.com>
Roger Pau Monné <royger@gmail.com>
Roger Peppe <rogpeppe@gmail.com>
Roland Shoemaker <rolandshoemaker@gmail.com>
Ron Hashimoto <mail@h2so5.net>
Ron Minnich <rminnich@gmail.com>
Ross Light <rlight2@gmail.com>
@ -567,8 +641,11 @@ Ryan Seys <ryan@ryanseys.com>
Ryan Slade <ryanslade@gmail.com>
S.Çağlar Onur <caglar@10ur.org>
Salmān Aljammāz <s@0x65.net>
Sam Hug <samuel.b.hug@gmail.com>
Sam Whited <sam@samwhited.com>
Sanjay Menakuru <balasanjay@gmail.com>
Scott Barron <scott.barron@github.com>
Scott Bell <scott@sctsm.com>
Scott Ferguson <scottwferg@gmail.com>
Scott Lawrence <bytbox@gmail.com>
Sebastien Binet <seb.binet@gmail.com>
@ -577,17 +654,21 @@ Sergei Skorobogatov <skorobo@rambler.ru>
Sergey 'SnakE' Gromov <snake.scaly@gmail.com>
Sergio Luis O. B. Correia <sergio@correia.cc>
Seth Hoenig <seth.a.hoenig@gmail.com>
Shahar Kohanim <skohanim@gmail.com>
Shane Hansen <shanemhansen@gmail.com>
Shaozhen Ding <dsz0111@gmail.com>
Shawn Smith <shawn.p.smith@gmail.com>
Shenghou Ma <minux.ma@gmail.com>
Shinji Tanaka <shinji.tanaka@gmail.com>
Shivakumar GN <shivakumar.gn@gmail.com>
Silvan Jegen <s.jegen@gmail.com>
Simon Jefford <simon.jefford@gmail.com>
Simon Whitehead <chemnova@gmail.com>
Sokolov Yura <funny.falcon@gmail.com>
Spencer Nelson <s@spenczar.com>
Spring Mc <heresy.mc@gmail.com>
Square, Inc.
Sridhar Venkatakrishnan <sridhar@laddoo.net>
StalkR <stalkr@stalkr.net>
Stan Schwertly <stan@schwertly.com>
Stefan Nilsson <snilsson@nada.kth.se> <trolleriprofessorn@gmail.com>
@ -605,6 +686,7 @@ Szabolcs Nagy <nsz@port70.net>
Tad Glines <tad.glines@gmail.com>
Taj Khattra <taj.khattra@gmail.com>
Takeshi YAMANASHI <9.nashi@gmail.com>
Tal Shprecher <tshprecher@gmail.com>
Tamir Duberstein <tamird@gmail.com>
Tarmigan Casebolt <tarmigan@gmail.com>
Taru Karttunen <taruti@taruti.net>
@ -615,9 +697,12 @@ Thomas Alan Copeland <talan.copeland@gmail.com>
Thomas Desrosiers <thomasdesr@gmail.com>
Thomas Kappler <tkappler@gmail.com>
Thorben Krueger <thorben.krueger@gmail.com>
Tilman Dilo <tilman.dilo@gmail.com>
Tim Cooijmans <timcooijmans@gmail.com>
Tim Ebringer <tim.ebringer@gmail.com>
Timo Savola <timo.savola@gmail.com>
Timo Truyts <alkaloid.btx@gmail.com>
Timothy Studd <tim@timstudd.com>
Tobias Columbus <tobias.columbus@gmail.com>
Todd Neal <todd@tneal.org>
Tom Heng <zhm20070928@gmail.com>
@ -634,12 +719,15 @@ Tyler Treat <ttreat31@gmail.com>
Ugorji Nwoke <ugorji@gmail.com>
Ulf Holm Nielsen <doktor@dyregod.dk>
Ulrich Kunitz <uli.kunitz@gmail.com>
Upthere, Inc.
Uriel Mangado <uriel@berlinblue.org>
Vadim Grek <vadimprog@gmail.com>
Vadim Vygonets <unixdj@gmail.com>
Vincent Ambo <tazjin@googlemail.com>
Vincent Batts <vbatts@hashbangbash.com> <vbatts@gmail.com>
Vincent Vanackere <vincent.vanackere@gmail.com>
Vinu Rajashekhar <vinutheraj@gmail.com>
Vishvananda Ishaya <vishvananda@gmail.com>
Vladimir Nikishenko <vova616@gmail.com>
Volker Dobler <dr.volker.dobler@gmail.com>
Wei Guangjing <vcc.163@gmail.com>
@ -648,6 +736,7 @@ William Josephson <wjosephson@gmail.com>
William Orr <will@worrbase.com> <ay1244@gmail.com>
Xia Bin <snyh@snyh.org>
Xing Xing <mikespook@gmail.com>
Xudong Zhang <felixmelon@gmail.com>
Yahoo Inc.
Yann Kerhervé <yann.kerherve@gmail.com>
Yao Zhang <lunaria21@gmail.com>
@ -661,6 +750,7 @@ Yoshiyuki Kanno <nekotaroh@gmail.com> <yoshiyuki.kanno@stoic.co.jp>
Yusuke Kagiwada <block.rxckin.beats@gmail.com>
Yuusei Kuwana <kuwana@kumama.org>
Yuval Pavel Zholkover <paulzhol@gmail.com>
Zemanta d.o.o.
Ziad Hatahet <hatahet@gmail.com>
Zorion Arrizabalaga <zorionk@gmail.com>
申习之 <bronze1man@gmail.com>

View file

@ -37,6 +37,7 @@ Aaron France <aaron.l.france@gmail.com>
Aaron Jacobs <jacobsa@google.com>
Aaron Kemp <kemp.aaron@gmail.com>
Aaron Torres <tcboox@gmail.com>
Abe Haskins <abeisgreat@abeisgreat.com>
Abhinav Gupta <abhinav.g90@gmail.com>
Adam Langley <agl@golang.org>
Adrian Nos <nos.adrian@gmail.com>
@ -44,7 +45,10 @@ Adrian O'Grady <elpollouk@gmail.com>
Adrien Bustany <adrien-xx-google@bustany.org>
Aécio Júnior <aeciodantasjunior@gmail.com>
Ahmed Waheed Moanes <oneofone@gmail.com>
Ahmy Yulrizka <yulrizka@gmail.com>
Aiden Scandella <ai@uber.com>
Ainar Garipov <gugl.zadolbal@gmail.com>
Akihiro Suda <suda.kyoto@gmail.com>
Akshat Kumar <seed@mail.nanosouffle.net>
Alan Donovan <adonovan@google.com>
Alan Shreve <alan@inconshreveable.com>
@ -53,6 +57,7 @@ Alberto Bertogli <albertito@blitiri.com.ar>
Alberto Donizetti <alb.donizetti@gmail.com>
Alberto García Hierro <alberto@garciahierro.com> <alberto.garcia.hierro@gmail.com>
Aleksandar Dezelin <dezelin@gmail.com>
Alessandro Arzilli <alessandro.arzilli@gmail.com>
Alex A Skinner <alex@lx.lc>
Alex Brainman <alex.brainman@gmail.com>
Alex Bramley <abramley@google.com>
@ -60,6 +65,7 @@ Alex Jin <toalexjin@gmail.com>
Alex Plugaru <alex@plugaru.org> <alexandru.plugaru@gmail.com>
Alex Schroeder <alex@gnu.org>
Alex Sergeyev <abc@alexsergeyev.com>
Alex Vaghin <crhyme@google.com>
Alexander Demakin <alexander.demakin@gmail.com>
Alexander Larsson <alexander.larsson@gmail.com>
Alexander Morozov <lk4d4math@gmail.com>
@ -80,6 +86,7 @@ Aliaksandr Valialkin <valyala@gmail.com>
Alif Rachmawadi <subosito@gmail.com>
Amir Mohammad Saied <amir@gluegadget.com>
Amrut Joshi <amrut.joshi@gmail.com>
Andre Nathan <andrenth@gmail.com>
Andrea Spadaccini <spadaccio@google.com>
Andreas Jellinghaus <andreas@ionisiert.de> <anj@google.com>
Andrei Korzhevskii <a.korzhevskiy@gmail.com>
@ -98,6 +105,7 @@ Andrew Pritchard <awpritchard@gmail.com>
Andrew Radev <andrey.radev@gmail.com>
Andrew Skiba <skibaa@gmail.com>
Andrew Szeto <andrew@jabagawee.com>
Andrew Werner <andrew@upthere.com> <awerner32@gmail.com>
Andrew Wilkins <axwalk@gmail.com>
Andrew Williams <williams.andrew@gmail.com>
Andrey Mirtchovski <mirtchovski@gmail.com>
@ -119,6 +127,7 @@ Apisak Darakananda <pongad@gmail.com>
Aram Hăvărneanu <aram@mgk.ro>
Areski Belaid <areski@gmail.com>
Arkadi Pyuro <arkadi@google.com>
Arlo Breault <arlolra@gmail.com>
Arnaud Ysmal <arnaud.ysmal@gmail.com>
Arne Hormann <arnehormann@gmail.com>
Arnout Engelen <arnout@bzzt.net>
@ -127,6 +136,8 @@ Artyom Pervukhin <artyom.pervukhin@gmail.com>
Arvindh Rajesh Tamilmani <art@a-30.net>
Asim Shankar <asimshankar@gmail.com>
Ato Araki <ato.araki@gmail.com>
Audrey Lim <audreylh@gmail.com>
Augusto Roman <aroman@gmail.com>
Aulus Egnatius Varialus <varialus@gmail.com>
Austin Clements <austin@google.com> <aclements@csail.mit.edu>
awaw fumin <awawfumin@gmail.com>
@ -139,6 +150,7 @@ Ben Lynn <benlynn@gmail.com>
Ben Olive <sionide21@gmail.com>
Benjamin Black <b@b3k.us>
Benjamin Prosnitz <bprosnitz@google.com>
Benjamin Wester <bwester@squareup.com>
Benny Siegert <bsiegert@gmail.com>
Benoit Sigoure <tsunanet@gmail.com>
Berengar Lehr <Berengar.Lehr@gmx.de>
@ -152,6 +164,8 @@ Blake Mizerany <blake.mizerany@gmail.com>
Bobby Powers <bobbypowers@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org> <bradfitz@gmail.com>
Brad Garcia <bgarcia@golang.org>
Brady Catherman <brady@gmail.com>
Brady Sullivan <brady@bsull.com>
Brandon Gilmore <varz@google.com>
Brendan Daniel Tracey <tracey.brendan@gmail.com>
Brendan O'Dea <bod@golang.org>
@ -163,8 +177,10 @@ Brian Ketelsen <bketelsen@gmail.com>
Brian Slesinsky <skybrian@google.com>
Brian Smith <ohohvi@gmail.com>
Bryan C. Mills <bcmills@google.com>
Bryan Chan <bryan.chan@ca.ibm.com>
Bryan Ford <brynosaurus@gmail.com>
Caine Tighe <arctanofyourface@gmail.com>
Caio Marcelo de Oliveira Filho <caio.oliveira@intel.com>
Caleb Spare <cespare@gmail.com>
Carl Chatfield <carlchatfield@gmail.com>
Carl Jackson <carl@stripe.com>
@ -175,6 +191,7 @@ Carlos Cirello <uldericofilho@gmail.com>
Cary Hull <chull@google.com>
Case Nelson <case.nelson@gmail.com>
Casey Marshall <casey.marshall@gmail.com>
Catalin Nicutar <cnicutar@google.com>
Catalin Patulea <catalinp@google.com>
Cedric Staub <cs@squareup.com>
Cezar Sá Espinola <cezarsa@gmail.com>
@ -182,6 +199,7 @@ ChaiShushan <chaishushan@gmail.com>
Charles L. Dorian <cldorian@gmail.com>
Charles Lee <zombie.fml@gmail.com>
Charles Weill <weill@google.com>
Cherry Zhang <cherryyz@google.com>
Chris Broadfoot <cbro@golang.org>
Chris Dollin <ehog.hedge@gmail.com>
Chris Farmiloe <chrisfarms@gmail.com>
@ -193,25 +211,31 @@ Chris Kastorff <encryptio@gmail.com>
Chris Lennert <calennert@gmail.com>
Chris Manghane <cmang@golang.org>
Chris McGee <sirnewton_01@yahoo.ca> <newton688@gmail.com>
Chris Zou <chriszou@ca.ibm.com>
Christian Himpel <chressie@googlemail.com> <chressie@gmail.com>
Christine Hansmann <chhansmann@gmail.com>
Christoffer Buchholz <christoffer.buchholz@gmail.com>
Christoph Hack <christoph@tux21b.org>
Christopher Cahoon <chris.cahoon@gmail.com>
Christopher Guiney <chris@guiney.net>
Christopher Nelson <nadiasvertex@gmail.com>
Christopher Nielsen <m4dh4tt3r@gmail.com>
Christopher Redden <christopher.redden@gmail.com>
Christopher Swenson <cswenson@google.com>
Christopher Wedgwood <cw@f00f.org>
Christy Perez <christy@linux.vnet.ibm.com>
CL Sung <clsung@gmail.com> <cl_sung@htc.com>
Clement Skau <clementskau@gmail.com>
Colby Ranger <cranger@google.com>
Colin Cross <ccross@android.com>
Colin Edwards <colin@recursivepenguin.com>
Colin Kennedy <moshen.colin@gmail.com>
Conrad Irwin <conrad.irwin@gmail.com>
Conrad Meyer <cemeyer@cs.washington.edu>
Corey Thomasson <cthom.lists@gmail.com>
Cosmos Nicolaou <cnicolaou@google.com>
Cristian Staretu <unclejacksons@gmail.com>
Cuihtlauac ALVARADO <cuihtlauac.alvarado@orange.com>
Damian Gryski <dgryski@gmail.com>
Damien Neil <dneil@google.com>
Dan Caddigan <goldcaddy77@gmail.com>
@ -229,8 +253,10 @@ Daniel Morsing <daniel.morsing@gmail.com>
Daniel Nadasi <dnadasi@google.com>
Daniel Ortiz Pereira da Silva <daniel.particular@gmail.com>
Daniel Skinner <daniel@dasa.cc>
Daniel Speichert <daniel@speichert.pl>
Daniel Theophanes <kardianos@gmail.com>
Darren Elwood <darren@textnode.com>
Datong Sun <dndx@idndx.com>
Dave Borowitz <dborowitz@google.com>
Dave Bort <dbort@golang.org>
Dave Cheney <dave@cheney.net>
@ -239,6 +265,7 @@ Dave Grijalva <dgrijalva@ngmoco.com>
David Anderson <danderson@google.com>
David Barnett <dbarnett@google.com>
David Benjamin <davidben@google.com>
David Brophy <dave@brophy.uk>
David Bürgin <676c7473@gmail.com>
David Calavera <david.calavera@gmail.com>
David Chase <drchase@google.com>
@ -254,6 +281,7 @@ David Leon Gil <coruus@gmail.com>
David McLeish <davemc@google.com>
David Presotto <presotto@gmail.com>
David R. Jenni <david.r.jenni@gmail.com>
David Sansome <me@davidsansome.com>
David Symonds <dsymonds@golang.org>
David Thomas <davidthomas426@gmail.com>
David Titarenco <david.titarenco@gmail.com>
@ -261,15 +289,19 @@ Davies Liu <davies.liu@gmail.com>
Dean Prichard <dean.prichard@gmail.com>
Denis Bernard <db047h@gmail.com>
Denis Brandolini <denis.brandolini@gmail.com>
Denys Honsiorovskyi <honsiorovskyi@gmail.com>
Derek Buitenhuis <derek.buitenhuis@gmail.com>
Derek Che <drc@yahoo-inc.com>
Derek Parker <parkerderek86@gmail.com>
Derek Shockey <derek.shockey@gmail.com>
Devon H. O'Dell <devon.odell@gmail.com>
Dhiru Kholia <dhiru.kholia@gmail.com>
Didier Spezia <didier.06@gmail.com>
Dimitri Tcaciuc <dtcaciuc@gmail.com>
Dirk Gadsden <dirk@esherido.com>
Diwaker Gupta <diwakergupta@gmail.com>
Dmitri Shuralyov <shurcooL@gmail.com>
Dmitriy Dudkin <dudkin.dmitriy@gmail.com>
Dmitriy Shelenin <deemok@googlemail.com> <deemok@gmail.com>
Dmitriy Vyukov <dvyukov@google.com>
Dmitry Chestnykh <dchest@gmail.com>
@ -279,8 +311,11 @@ Dominik Honnef <dominik.honnef@gmail.com>
Dominik Vogt <vogt@linux.vnet.ibm.com>
Donald Huang <don.hcd@gmail.com>
Donovan Hide <donovanhide@gmail.com>
Doug Anderson <douga@google.com>
Drew Hintz <adhintz@google.com>
Duncan Holm <mail@frou.org>
Dustin Carlino <dcarlino@google.com>
Dustin Herbison <djherbis@gmail.com>
Dustin Long <dustmop@gmail.com>
Dustin Sallings <dsallings@gmail.com>
Dustin Shields-Cloues <dcloues@gmail.com>
@ -304,7 +339,9 @@ Erik Aigner <aigner.erik@gmail.com>
Erik Dubbelboer <erik@dubbelboer.com>
Erik St. Martin <alakriti@gmail.com>
Erik Westrup <erik.westrup@gmail.com>
Ernest Chiang <ernest_chiang@htc.com>
Esko Luontola <esko.luontola@gmail.com>
Ethan Burns <eaburns@google.com>
Evan Broder <evan@stripe.com>
Evan Brown <evanbrown@google.com>
Evan Kroske <evankroske@google.com>
@ -331,11 +368,13 @@ Francisco Souza <franciscossouza@gmail.com>
Frederick Kelly Mayle III <frederickmayle@gmail.com>
Fredrik Enestad <fredrik.enestad@soundtrackyourbrand.com>
Frithjof Schulze <schulze@math.uni-hannover.de> <sfrithjof@gmail.com>
Frits van Bommel <fvbommel@gmail.com>
Fumitoshi Ukai <ukai@google.com>
Gaal Yahas <gaal@google.com>
Gabriel Aszalos <gabriel.aszalos@gmail.com>
Garrick Evans <garrick@google.com>
Gary Burd <gary@beagledreams.com> <gary.burd@gmail.com>
Gary Elliott <garyelliott@google.com>
Gaurish Sharma <contact@gaurishsharma.com>
Gautham Thambidorai <gautham.dorai@gmail.com>
Geert-Johan Riemer <gjr19912@gmail.com>
@ -359,17 +398,22 @@ Gustavo Franco <gustavorfranco@gmail.com>
Gustavo Niemeyer <gustavo@niemeyer.net> <n13m3y3r@gmail.com>
Gwenael Treguier <gwenn.kahz@gmail.com>
Hajime Hoshi <hajimehoshi@gmail.com>
Hallgrimur Gunnarsson <halg@google.com>
Han-Wen Nienhuys <hanwen@google.com>
Hari haran <hariharan.uno@gmail.com>
Hariharan Srinath <srinathh@gmail.com>
Harley Laue <losinggeneration@gmail.com>
Harshavardhana <hrshvardhana@gmail.com>
Håvard Haugen <havard.haugen@gmail.com>
Hector Chu <hectorchu@gmail.com>
Hector Martin Cantero <hector@marcansoft.com>
Henning Schmiedehausen <henning@schmiedehausen.org>
Henrik Edwards <henrik.edwards@gmail.com>
Herbert Georg Fischer <herbert.fischer@gmail.com>
Hironao OTSUBO <motemen@gmail.com>
Hiroshi Ioka <hirochachacha@gmail.com>
Hitoshi Mitake <mitake.hitoshi@gmail.com>
Holden Huang <ttyh061@gmail.com>
Hong Ruiqi <hongruiqi@gmail.com>
Hossein Sheikh Attar <hattar@google.com>
Hsin-Ho Yeh <yhh92u@gmail.com>
@ -378,11 +422,13 @@ Hyang-Ah Hana Kim <hakim@google.com> <hyangah@gmail.com>
Ian Gudger <ian@loosescre.ws>
Ian Lance Taylor <iant@golang.org>
Icarus Sparry <golang@icarus.freeuk.com>
Idora Shinatose <idora.shinatose@gmail.com>
Igor Dolzhikov <bluesriverz@gmail.com>
Ilya Tocar <ilya.tocar@intel.com>
INADA Naoki <songofacandy@gmail.com>
Ingo Krabbe <ikrabbe.ask@gmail.com>
Ingo Oeser <nightlyone@googlemail.com> <nightlyone@gmail.com>
Irieda Noboru <irieda@gmail.com>
Isaac Wagner <ibw@isaacwagner.me>
Ivan Krasin <krasin@golang.org>
Ivan Ukhov <ivan.ukhov@gmail.com>
@ -394,6 +440,8 @@ Jakob Borg <jakob@nym.se>
Jakub Čajka <jcajka@redhat.com>
Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com>
James Aguilar <jaguilar@google.com>
James Bardin <j.bardin@gmail.com>
James Chacon <jchacon@google.com>
James David Chalfant <james.chalfant@gmail.com>
James Fysh <james.fysh@gmail.com>
James Gray <james@james4k.com>
@ -408,6 +456,7 @@ James Whitehead <jnwhiteh@gmail.com>
Jamie Gennis <jgennis@google.com> <jgennis@gmail.com>
Jamie Turner <jamwt@dropbox.com>
Jamie Wilkinson <jaq@spacepants.org>
Jamil Djadala <djadala@gmail.com>
Jan H. Hosang <jan.hosang@gmail.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
Jan Mercl <0xjnml@gmail.com>
@ -422,6 +471,7 @@ Jason Travis <infomaniac7@gmail.com>
Jay Weisskopf <jay@jayschwa.net>
Jean-Marc Eurin <jmeurin@google.com>
Jed Denlea <jed@fastly.com>
Jeff Craig <jeffcraig@google.com>
Jeff Hodges <jeff@somethingsimilar.com>
Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com>
Jeff Sickel <jas@corpus-callosum.com>
@ -430,6 +480,7 @@ Jens Frederich <jfrederich@gmail.com>
Jeremiah Harmsen <jeremiah@google.com>
Jeremy Jackins <jeremyjackins@gmail.com>
Jeremy Schlatter <jeremy.schlatter@gmail.com>
Jess Frazelle <me@jessfraz.com>
Jihyun Yu <yjh0502@gmail.com>
Jim Cote <jfcote87@gmail.com>
Jim McGrath <jimmc2@gmail.com>
@ -439,12 +490,15 @@ Jingguo Yao <yaojingguo@gmail.com>
Jiong Du <londevil@gmail.com>
Joakim Sernbrant <serbaut@gmail.com>
Joe Harrison <joehazzers@gmail.com>
Joe Henke <joed.henke@gmail.com>
Joe Poirier <jdpoirier@gmail.com>
Joe Shaw <joe@joeshaw.org>
Joe Sylve <joe.sylve@gmail.com>
Joe Tsai <joetsai@digital-static.net>
Joel Sing <jsing@google.com>
Joel Stemmer <stemmertech@gmail.com>
Johan Euphrosine <proppy@google.com>
Johan Sageryd <j@1616.se>
John Asmuth <jasmuth@gmail.com>
John Beisley <huin@google.com>
John C Barstow <jbowtie@amathaine.com>
@ -452,12 +506,15 @@ John DeNero <denero@google.com>
John Dethridge <jcd@golang.org>
John Graham-Cumming <jgc@jgc.org> <jgrahamc@gmail.com>
John Howard Palevich <jack.palevich@gmail.com>
John Jeffery <jjeffery@sp.com.au>
John Jenkins <twodopeshaggy@gmail.com>
John Newlin <jnewlin@google.com>
John Potocny <johnp@vividcortex.com>
John Schnake <schnake.john@gmail.com>
John Shahid <jvshahid@gmail.com>
John Tuley <john@tuley.org>
Jonathan Allie <jonallie@google.com>
Jonathan Amsterdam <jba@google.com>
Jonathan Boulle <jonathanboulle@gmail.com>
Jonathan Feinberg <feinberg@google.com>
Jonathan Gold <jgold.bg@gmail.com>
@ -481,11 +538,14 @@ Jostein Stuhaug <js@solidsystem.no>
JP Sugarbroad <jpsugar@google.com>
JT Olds <jtolds@xnet5.com>
Jukka-Pekka Kekkonen <karatepekka@gmail.com>
Julia Hansbrough <flowerhack@google.com>
Julian Phillips <julian@quantumfyre.co.uk>
Julien Schmidt <google@julienschmidt.com>
Jungho Ahn <jhahn@google.com>
Jure Ham <jure.ham@zemanta.com>
Justin Nuß <nuss.justin@gmail.com>
Kai Backman <kaib@golang.org>
Kamal Aboul-Hosn <aboulhosn@google.com>
Kamil Kisiel <kamil@kamilkisiel.net> <kamil.kisiel@gmail.com>
Kang Hu <hukangustc@gmail.com>
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
@ -502,10 +562,15 @@ Ken Friedenbach <kenliz@cruzio.com>
Ken Rockot <ken@oz.gs> <ken.rockot@gmail.com>
Ken Sedgwick <ken@bonsai.com>
Ken Thompson <ken@golang.org>
Kenji Kaneda <kenji.kaneda@gmail.com>
Kenneth Shaw <kenshaw@gmail.com>
Kenny Grant <kennygrant@gmail.com>
Kevin Ballard <kevin@sb.org>
Kevin Burke <kev@inburke.com>
Kevin Kirsche <kev.kirsche@gmail.com>
Kevin Klues <klueska@gmail.com> <klueska@google.com>
Kevin Malachowski <chowski@google.com>
Kevin Vu <kevin.m.vu@gmail.com>
Kim Shrier <kshrier@racktopsystems.com>
Kirklin McDonald <kirklin.mcdonald@gmail.com>
Klaus Post <klauspost@gmail.com>
@ -519,11 +584,13 @@ L Campbell <unpantsu@gmail.com>
Lai Jiangshan <eag0628@gmail.com>
Larry Hosken <lahosken@golang.org>
Larz Conwell <larzconwell@gmail.com>
Lee Hinman <hinman@gmail.com>
Lee Packham <lpackham@gmail.com>
Lewin Bormann <lewin.bormann@gmail.com>
Lloyd Dewolf <foolswisdom@gmail.com>
Lorenzo Stoakes <lstoakes@gmail.com>
Louis Kruger <louisk@google.com>
Luan Santos <cfcluan@gmail.com>
Luca Greco <luca.greco@alcacoop.it>
Lucien Stuker <lucien.stuker@gmail.com>
Lucio De Re <lucio.dere@gmail.com>
@ -539,11 +606,13 @@ Manu Garg <manugarg@google.com>
Manu S Ajith <neo@codingarena.in>
Manuel Mendez <mmendez534@gmail.com>
Marc Weistroff <marc@weistroff.net>
Marc-Antoine Ruel <maruel@chromium.org>
Marcel van Lohuizen <mpvl@golang.org>
Marco Hennings <marco.hennings@freiheit.com>
Marga Manterola <marga@google.com>
Marius Nuennerich <mnu@google.com>
Mark Bucciarelli <mkbucc@gmail.com>
Mark Severson <miquella@gmail.com>
Mark Theunissen <mark.theunissen@gmail.com>
Mark Zavislak <zavislak@google.com>
Marko Juhani Silokunnas <marko.silokunnas@gmail.com>
@ -552,12 +621,14 @@ Marko Tiikkaja <marko@joh.to>
Markus Duft <markus.duft@salomon.at>
Markus Sonderegger <marraison@gmail.com>
Markus Zimmermann <zimmski@gmail.com>
Martin Garton <garton@gmail.com>
Martin Möhrmann <martisch@uos.de>
Martin Neubauer <m.ne@gmx.net>
Martin Olsson <martin@minimum.se>
Marvin Stenger <marvin.stenger94@gmail.com>
Mateusz Czapliński <czapkofan@gmail.com>
Mathias Beke <git@denbeke.be>
Mathias Leppich <mleppich@muhqu.de>
Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
Mats Lidell <mats.lidell@cag.se> <mats.lidell@gmail.com>
Matt Aimonetti <mattaimonetti@gmail.com>
@ -569,6 +640,7 @@ Matt Joiner <anacrolix@gmail.com>
Matt Jones <mrjones@google.com>
Matt Layher <mdlayher@gmail.com>
Matt Reiferson <mreiferson@gmail.com>
Matt Robenolt <matt@ydekproductions.com>
Matt T. Proud <matt.proud@gmail.com>
Matt Williams <gh@mattyw.net> <mattyjwilliams@gmail.com>
Matthew Brennan <matty.brennan@gmail.com>
@ -579,6 +651,7 @@ Matthew Horsnell <matthew.horsnell@gmail.com>
Maxim Khitrov <max@mxcrypt.com>
Maxim Pimenov <mpimenov@google.com>
Maxim Ushakov <ushakov@google.com>
Maxwell Krohn <themax@gmail.com>
Meir Fischer <meirfischer@gmail.com>
Meng Zhuo <mengzhuo1203@gmail.com>
Mhd Sulhan <m.shulhan@gmail.com>
@ -595,9 +668,12 @@ Michael Lewis <mikelikespie@gmail.com>
Michael MacInnis <Michael.P.MacInnis@gmail.com>
Michael Marineau <michael.marineau@coreos.com>
Michael Matloob <matloob@google.com>
Michael McConville <momcconville@gmail.com>
Michael McGreevy <mcgreevy@golang.org>
Michael Munday <munday@ca.ibm.com>
Michael Pearson <mipearson@gmail.com>
Michael Piatek <piatek@google.com>
Michael Pratt <mpratt@google.com>
Michael Schaller <michael@5challer.de>
Michael Shields <mshields@google.com>
Michael Stapelberg <michael@stapelberg.de> <mstplbrg@googlemail.com>
@ -608,22 +684,28 @@ Michal Bohuslávek <mbohuslavek@gmail.com>
Michal Cierniak <cierniak@google.com>
Michał Derkacz <ziutek@lnet.pl>
Michalis Kargakis <michaliskargakis@gmail.com>
Michel Lespinasse <walken@google.com>
Miek Gieben <miek@miek.nl> <remigius.gieben@gmail.com>
Mihai Borobocea <MihaiBorobocea@gmail.com>
Mikael Tillenius <mikti42@gmail.com>
Mike Andrews <mra@xoba.com>
Mike Danese <mikedanese@google.com>
Mike Rosset <mike.rosset@gmail.com>
Mike Samuel <mikesamuel@gmail.com>
Mike Solomon <msolo@gmail.com>
Mikhail Gusarov <dottedmag@dottedmag.net>
Mikhail Panchenko <m@mihasya.com>
Miki Tebeka <miki.tebeka@gmail.com>
Mikio Hara <mikioh.mikioh@gmail.com>
Mikkel Krautz <mikkel@krautz.dk> <krautz@gmail.com>
Miquel Sabaté Solà <mikisabate@gmail.com>
Mohit Agarwal <mohit@sdf.org>
Monty Taylor <mordred@inaugust.com>
Moriyoshi Koizumi <mozo@mozo.jp>
Morten Siebuhr <sbhr@sbhr.dk>
Môshe van der Sterre <moshevds@gmail.com>
Mrunal Patel <mrunalp@gmail.com>
Muhammed Uluyol <uluyol0@gmail.com>
Nan Deng <monnand@gmail.com>
Nathan John Youngman <nj@nathany.com>
Nathan Otterness <otternes@cs.unc.edu>
@ -633,17 +715,22 @@ Nathan Youngman <git@nathany.com>
Nathan(yinian) Hu <nathanhu@google.com>
Neelesh Chandola <neelesh.c98@gmail.com>
Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
Niall Sheridan <nsheridan@gmail.com>
Nicholas Katsaros <nick@nickkatsaros.com>
Nicholas Presta <nick@nickpresta.ca> <nick1presta@gmail.com>
Nicholas Sullivan <nicholas.sullivan@gmail.com>
Nicholas Waples <nwaples@gmail.com>
Nick Cooper <nmvc@google.com>
Nick Craig-Wood <nick@craig-wood.com> <nickcw@gmail.com>
Nick Patavalis <nick.patavalis@gmail.com>
Nick Petroni <npetroni@cs.umd.edu>
Nicolas Kaiser <nikai@nikai.net>
Nicolas Owens <mischief@offblast.org>
Nicolas S. Dade <nic.dade@gmail.com>
Niels Widger <niels.widger@gmail.com>
Nigel Kerr <nigel.kerr@gmail.com>
Nigel Tao <nigeltao@golang.org>
Niko Dziemba <niko@dziemba.com>
Nikolay Turpitko <nikolay@turpitko.com>
Noah Campbell <noahcampbell@gmail.com>
Nodir Turakulov <nodir@google.com>
@ -653,7 +740,10 @@ Oling Cat <olingcat@gmail.com>
Oliver Hookins <ohookins@gmail.com>
Olivier Antoine <olivier.antoine@gmail.com>
Olivier Duperray <duperray.olivier@gmail.com>
Olivier Poitrey <rs@dailymotion.com>
Olivier Saingre <osaingre@gmail.com>
Omar Jarjur <ojarjur@google.com>
Özgür Kesim <oec-go@kesim.org>
Padraig Kitterick <padraigkitterick@gmail.com>
Paolo Giarrusso <p.giarrusso@gmail.com>
Paolo Martini <mrtnpaolo@gmail.com>
@ -678,6 +768,7 @@ Paul Rosania <paul.rosania@gmail.com>
Paul Sbarra <Sbarra.Paul@gmail.com>
Paul Smith <paulsmith@pobox.com> <paulsmith@gmail.com>
Paul van Brouwershaven <paul@vanbrouwershaven.com>
Paul Wankadia <junyer@google.com>
Pavel Paulau <pavel.paulau@gmail.com>
Pavel Zinovkin <pavel.zinovkin@gmail.com>
Pawel Knap <pawelknap88@gmail.com>
@ -688,6 +779,7 @@ Petar Maymounkov <petarm@gmail.com>
Peter Armitage <peter.armitage@gmail.com>
Peter Collingbourne <pcc@google.com>
Peter Froehlich <peter.hans.froehlich@gmail.com>
Peter Gonda <pgonda@google.com>
Peter Kleiweg <pkleiweg@xs4all.nl>
Peter McKenzie <petermck@google.com>
Peter Moody <pmoody@uber.com>
@ -701,13 +793,17 @@ Peter Waller <peter.waller@gmail.com>
Peter Weinberger <pjw@golang.org>
Peter Williams <pwil3058@gmail.com>
Phil Pennock <pdp@golang.org>
Philip Hofer <phofer@umich.edu>
Philip K. Warren <pkwarren@gmail.com>
Pierre Durand <pierredurand@gmail.com>
Pierre Roullon <pierre.roullon@gmail.com>
Pieter Droogendijk <pieter@binky.org.uk>
Pietro Gagliardi <pietro10@mac.com>
Prashant Varanasi <prashant@prashantv.com>
Preetam Jinka <pj@preet.am>
Quan Yong Zhai <qyzhai@gmail.com>
Quentin Perez <qperez@ocs.online.net>
Quentin Smith <quentin@golang.org>
Quoc-Viet Nguyen <afelion@gmail.com>
Rahul Chaudhry <rahulchaudhry@chromium.org>
Raif S. Naffah <go@naffah-raif.name>
@ -717,12 +813,16 @@ Raph Levien <raph@google.com>
Raul Silvera <rsilvera@google.com>
Reinaldo de Souza Jr <juniorz@gmail.com>
Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
Rhys Hiltner <rhys@justin.tv>
Ricardo Padilha <ricardospadilha@gmail.com>
Richard Barnes <rlb@ipv.sx>
Richard Crowley <r@rcrowley.org>
Richard Eric Gavaletz <gavaletz@gmail.com>
Richard Miller <miller.research@gmail.com>
Richard Musiol <mail@richard-musiol.de> <neelance@gmail.com>
Rick Arnold <rickarnoldjr@gmail.com>
Rick Hudson <rlh@golang.org>
Riku Voipio <riku.voipio@linaro.org>
Risto Jaakko Saarelma <rsaarelm@gmail.com>
Rob Earhart <earhart@google.com>
Rob Norman <rob.norman@infinitycloud.com>
@ -742,6 +842,7 @@ Rodrigo Moraes de Oliveira <rodrigo.moraes@gmail.com>
Rodrigo Rafael Monti Kochenburger <divoxx@gmail.com>
Roger Pau Monné <royger@gmail.com>
Roger Peppe <rogpeppe@gmail.com>
Roland Shoemaker <rolandshoemaker@gmail.com>
Ron Hashimoto <mail@h2so5.net>
Ron Minnich <rminnich@gmail.com>
Ross Light <light@google.com> <rlight2@gmail.com>
@ -757,17 +858,23 @@ Ryan Seys <ryan@ryanseys.com>
Ryan Slade <ryanslade@gmail.com>
S.Çağlar Onur <caglar@10ur.org>
Salmān Aljammāz <s@0x65.net>
Sam Hug <samuel.b.hug@gmail.com>
Sam Thorogood <thorogood@google.com> <sam.thorogood@gmail.com>
Sam Whited <sam@samwhited.com>
Sameer Ajmani <sameer@golang.org> <ajmani@gmail.com>
Sami Commerot <samic@google.com>
Sanjay Menakuru <balasanjay@gmail.com>
Sasha Lionheart <lionhearts@google.com>
Scott Barron <scott.barron@github.com>
Scott Bell <scott@sctsm.com>
Scott Ferguson <scottwferg@gmail.com>
Scott Lawrence <bytbox@gmail.com>
Scott Mansfield <smansfield@netflix.com>
Scott Schwartz <scotts@golang.org>
Scott Van Woudenberg <scottvw@google.com>
Sean Burford <sburford@google.com>
Sean Dolphin <Sean.Dolphin@kpcompass.com>
Sean Harger <sharger@google.com>
Sebastien Binet <seb.binet@gmail.com>
Sébastien Paolacci <sebastien.paolacci@gmail.com>
Sergei Skorobogatov <skorobo@rambler.ru>
@ -775,20 +882,24 @@ Sergey 'SnakE' Gromov <snake.scaly@gmail.com>
Sergey Arseev <sergey.arseev@intel.com>
Sergio Luis O. B. Correia <sergio@correia.cc>
Seth Hoenig <seth.a.hoenig@gmail.com>
Shahar Kohanim <skohanim@gmail.com>
Shane Hansen <shanemhansen@gmail.com>
Shaozhen Ding <dsz0111@gmail.com>
Shawn Ledbetter <sledbetter@google.com>
Shawn Smith <shawn.p.smith@gmail.com>
Shawn Walker-Salas <shawn.walker@oracle.com>
Shenghou Ma <minux@golang.org> <minux.ma@gmail.com>
Shinji Tanaka <shinji.tanaka@gmail.com>
Shivakumar GN <shivakumar.gn@gmail.com>
Shun Fan <sfan@google.com>
Silvan Jegen <s.jegen@gmail.com>
Simon Jefford <simon.jefford@gmail.com>
Simon Whitehead <chemnova@gmail.com>
Sokolov Yura <funny.falcon@gmail.com>
Spencer Nelson <s@spenczar.com>
Spring Mc <heresy.mc@gmail.com>
Srdjan Petrovic <spetrovic@google.com>
Sridhar Venkatakrishnan <sridhar@laddoo.net>
StalkR <stalkr@stalkr.net>
Stan Schwertly <stan@schwertly.com>
Stefan Nilsson <snilsson@nada.kth.se> <trolleriprofessorn@gmail.com>
@ -803,12 +914,14 @@ Steve Streeting <steve@stevestreeting.com>
Steven Elliot Harris <seharris@gmail.com>
Steven Hartland <steven.hartland@multiplay.co.uk>
Sugu Sougoumarane <ssougou@gmail.com>
Suharsh Sivakumar <suharshs@google.com>
Sven Almgren <sven@tras.se>
Szabolcs Nagy <nsz@port70.net>
Tad Glines <tad.glines@gmail.com>
Taj Khattra <taj.khattra@gmail.com>
Takashi Matsuo <tmatsuo@google.com>
Takeshi YAMANASHI <9.nashi@gmail.com>
Tal Shprecher <tshprecher@gmail.com>
Tamir Duberstein <tamird@gmail.com>
Tarmigan Casebolt <tarmigan@gmail.com>
Taru Karttunen <taruti@taruti.net>
@ -820,13 +933,20 @@ Thomas Desrosiers <thomasdesr@gmail.com>
Thomas Habets <habets@google.com>
Thomas Kappler <tkappler@gmail.com>
Thorben Krueger <thorben.krueger@gmail.com>
Tilman Dilo <tilman.dilo@gmail.com>
Tim Cooijmans <timcooijmans@gmail.com>
Tim Ebringer <tim.ebringer@gmail.com>
Tim Hockin <thockin@google.com>
Tim Swast <swast@google.com>
Timo Savola <timo.savola@gmail.com>
Timo Truyts <alkaloid.btx@gmail.com>
Timothy Studd <tim@timstudd.com>
Tipp Moseley <tipp@google.com>
Tobias Columbus <tobias.columbus@gmail.com> <tobias.columbus@googlemail.com>
Toby Burress <kurin@google.com>
Todd Neal <todd@tneal.org>
Todd Wang <toddwang@gmail.com>
Tom Bergan <tombergan@google.com>
Tom Heng <zhm20070928@gmail.com>
Tom Linford <tomlinford@gmail.com>
Tom Szymanski <tgs@google.com>
@ -840,11 +960,13 @@ Trey Tacon <ttacon@gmail.com>
Tudor Golubenco <tudor.g@gmail.com>
Tyler Bunnell <tylerbunnell@gmail.com>
Tyler Treat <ttreat31@gmail.com>
Tzu-Jung Lee <roylee17@currant.com>
Ugorji Nwoke <ugorji@gmail.com>
Ulf Holm Nielsen <doktor@dyregod.dk>
Ulrich Kunitz <uli.kunitz@gmail.com>
Uriel Mangado <uriel@berlinblue.org>
Uttam C Pawar <uttam.c.pawar@intel.com>
Vadim Grek <vadimprog@gmail.com>
Vadim Vygonets <unixdj@gmail.com>
Vega Garcia Luis Alfonso <vegacom@gmail.com>
Vincent Ambo <tazjin@googlemail.com>
@ -852,9 +974,11 @@ Vincent Batts <vbatts@hashbangbash.com> <vbatts@gmail.com>
Vincent Vanackere <vincent.vanackere@gmail.com>
Vinu Rajashekhar <vinutheraj@gmail.com>
Vish Subramanian <vish@google.com>
Vishvananda Ishaya <vishvananda@gmail.com>
Vlad Krasnov <vlad@cloudflare.com>
Vladimir Nikishenko <vova616@gmail.com>
Volker Dobler <dr.volker.dobler@gmail.com>
Wedson Almeida Filho <wedsonaf@google.com>
Wei Guangjing <vcc.163@gmail.com>
Will Chan <willchan@google.com>
Will Norris <willnorris@google.com>
@ -864,6 +988,7 @@ William Josephson <wjosephson@gmail.com>
William Orr <will@worrbase.com> <ay1244@gmail.com>
Xia Bin <snyh@snyh.org>
Xing Xing <mikespook@gmail.com>
Xudong Zhang <felixmelon@gmail.com>
Yan Zou <yzou@google.com>
Yann Kerhervé <yann.kerherve@gmail.com>
Yao Zhang <lunaria21@gmail.com>
@ -879,6 +1004,7 @@ Yusuke Kagiwada <block.rxckin.beats@gmail.com>
Yuusei Kuwana <kuwana@kumama.org>
Yuval Pavel Zholkover <paulzhol@gmail.com>
Yves Junqueira <yvesj@google.com> <yves.junqueira@gmail.com>
Zhongwei Yao <zhongwei.yao@arm.com>
Ziad Hatahet <hatahet@gmail.com>
Zorion Arrizabalaga <zorionk@gmail.com>
申习之 <bronze1man@gmail.com>

View file

@ -17,7 +17,15 @@ pkg context, type Context interface, Err() error
pkg context, type Context interface, Value(interface{}) interface{}
pkg context, var Canceled error
pkg context, var DeadlineExceeded error
pkg crypto/tls, const RenegotiateFreelyAsClient = 2
pkg crypto/tls, const RenegotiateFreelyAsClient RenegotiationSupport
pkg crypto/tls, const RenegotiateNever = 0
pkg crypto/tls, const RenegotiateNever RenegotiationSupport
pkg crypto/tls, const RenegotiateOnceAsClient = 1
pkg crypto/tls, const RenegotiateOnceAsClient RenegotiationSupport
pkg crypto/tls, type Config struct, DynamicRecordSizingDisabled bool
pkg crypto/tls, type Config struct, Renegotiation RenegotiationSupport
pkg crypto/tls, type RenegotiationSupport int
pkg crypto/x509, func SystemCertPool() (*CertPool, error)
pkg crypto/x509, type SystemRootsError struct, Err error
pkg debug/dwarf, method (*Data) Ranges(*Entry) ([][2]uint64, error)
@ -147,8 +155,9 @@ pkg debug/elf, const R_390_TLS_TPOFF R_390
pkg debug/elf, method (R_390) GoString() string
pkg debug/elf, method (R_390) String() string
pkg debug/elf, type R_390 int
pkg encoding/json, method (*Encoder) DisableHTMLEscaping()
pkg encoding/json, method (*Encoder) Indent(string, string)
pkg encoding/json, method (*Encoder) SetEscapeHTML(bool)
pkg encoding/json, method (*Encoder) SetIndent(string, string)
pkg go/build, type Package struct, BinaryOnly bool
pkg go/build, type Package struct, CgoFFLAGS []string
pkg go/build, type Package struct, FFiles []string
pkg go/doc, type Example struct, Unordered bool
@ -158,22 +167,54 @@ pkg io, const SeekEnd = 2
pkg io, const SeekEnd ideal-int
pkg io, const SeekStart = 0
pkg io, const SeekStart ideal-int
pkg io, type SizedReaderAt interface { ReadAt, Size }
pkg io, type SizedReaderAt interface, ReadAt([]uint8, int64) (int, error)
pkg io, type SizedReaderAt interface, Size() int64
pkg math/big, method (*Float) GobDecode([]uint8) error
pkg math/big, method (*Float) GobEncode() ([]uint8, error)
pkg net, method (*Dialer) DialContext(context.Context, string, string) (Conn, error)
pkg net, type IPNet struct, Zone string
pkg net/http, method (*Request) Context() context.Context
pkg net/http, method (*Request) WithContext(context.Context) *Request
pkg net/http, type Request struct, Response *Response
pkg net/http, type Response struct, Uncompressed bool
pkg net/http, type Transport struct, Dialer *net.Dialer
pkg net/http, type Transport struct, IdleConnTimeout time.Duration
pkg net/http, type Transport struct, MaxIdleConns int
pkg net/http, type Transport struct, MaxResponseHeaderBytes int64
pkg net/http, var ErrUseLastResponse error
pkg net/http, var LocalAddrContextKey *contextKey
pkg net/http, var ServerContextKey *contextKey
pkg net/http/cgi, type Handler struct, Stderr io.Writer
pkg net/http/httptest, func NewRequest(string, string, io.Reader) *http.Request
pkg net/http/httptest, method (*ResponseRecorder) Trailers() http.Header
pkg net/http/httptest, method (*ResponseRecorder) Result() *http.Response
pkg net/http/httptrace, func ContextClientTrace(context.Context) *ClientTrace
pkg net/http/httptrace, func WithClientTrace(context.Context, *ClientTrace) context.Context
pkg net/http/httptrace, type ClientTrace struct
pkg net/http/httptrace, type ClientTrace struct, ConnectDone func(string, string, error)
pkg net/http/httptrace, type ClientTrace struct, ConnectStart func(string, string)
pkg net/http/httptrace, type ClientTrace struct, DNSDone func(DNSDoneInfo)
pkg net/http/httptrace, type ClientTrace struct, DNSStart func(DNSStartInfo)
pkg net/http/httptrace, type ClientTrace struct, GetConn func(string)
pkg net/http/httptrace, type ClientTrace struct, Got100Continue func()
pkg net/http/httptrace, type ClientTrace struct, GotConn func(GotConnInfo)
pkg net/http/httptrace, type ClientTrace struct, GotFirstResponseByte func()
pkg net/http/httptrace, type ClientTrace struct, PutIdleConn func(error)
pkg net/http/httptrace, type ClientTrace struct, Wait100Continue func()
pkg net/http/httptrace, type ClientTrace struct, WroteHeaders func()
pkg net/http/httptrace, type ClientTrace struct, WroteRequest func(WroteRequestInfo)
pkg net/http/httptrace, type DNSDoneInfo struct
pkg net/http/httptrace, type DNSDoneInfo struct, Addrs []net.IPAddr
pkg net/http/httptrace, type DNSDoneInfo struct, Coalesced bool
pkg net/http/httptrace, type DNSDoneInfo struct, Err error
pkg net/http/httptrace, type DNSStartInfo struct
pkg net/http/httptrace, type DNSStartInfo struct, Host string
pkg net/http/httptrace, type GotConnInfo struct
pkg net/http/httptrace, type GotConnInfo struct, Conn net.Conn
pkg net/http/httptrace, type GotConnInfo struct, IdleTime time.Duration
pkg net/http/httptrace, type GotConnInfo struct, Reused bool
pkg net/http/httptrace, type GotConnInfo struct, WasIdle bool
pkg net/http/httptrace, type WroteRequestInfo struct
pkg net/http/httptrace, type WroteRequestInfo struct, Err error
pkg net/url, type URL struct, ForceQuery bool
pkg os, method (*File) Size() (int64, error)
pkg os/exec, func CommandContext(context.Context, string, ...string) *Cmd
pkg os/user, func LookupGroup(string) (*Group, error)
pkg os/user, func LookupGroupId(string) (*Group, error)
pkg os/user, method (*User) GroupIds() ([]string, error)
@ -187,6 +228,7 @@ pkg os/user, type UnknownGroupIdError string
pkg reflect, func StructOf([]StructField) Type
pkg reflect, method (StructTag) Lookup(string) (string, bool)
pkg runtime, func CallersFrames([]uintptr) *Frames
pkg runtime, func KeepAlive(interface{})
pkg runtime, func SetCgoTraceback(int, unsafe.Pointer, unsafe.Pointer, unsafe.Pointer)
pkg runtime, method (*Frames) Next() (Frame, bool)
pkg runtime, type Frame struct
@ -198,6 +240,12 @@ pkg runtime, type Frame struct, Line int
pkg runtime, type Frame struct, PC uintptr
pkg runtime, type Frames struct
pkg strings, method (*Reader) Reset(string)
pkg syscall (linux-386), type SysProcAttr struct, Unshare uintptr
pkg syscall (linux-386-cgo), type SysProcAttr struct, Unshare uintptr
pkg syscall (linux-amd64), type SysProcAttr struct, Unshare uintptr
pkg syscall (linux-amd64-cgo), type SysProcAttr struct, Unshare uintptr
pkg syscall (linux-arm), type SysProcAttr struct, Unshare uintptr
pkg syscall (linux-arm-cgo), type SysProcAttr struct, Unshare uintptr
pkg testing, method (*B) Run(string, func(*B)) bool
pkg testing, method (*T) Run(string, func(*T)) bool
pkg testing, type InternalExample struct, Unordered bool

View file

@ -89,7 +89,7 @@ gofmt</a> command with more general options.</td>
</tr>
<tr>
<td><a href="//godoc.org/golang.org/x/tools/cmd/vet/">vet</a></td>
<td><a href="/cmd/vet/">vet</a></td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string.</td>

View file

@ -353,10 +353,13 @@ with a thorough description of your change.
The first line of the change description is conventionally a one-line
summary of the change, prefixed by the primary affected package,
and is used as the subject for code review mail.
The rest of the
description elaborates and should provide context for the
It should complete the sentence "This change modifies Go to _____."
The rest of the description elaborates and should provide context for the
change and explain what it does.
Write in complete sentences with correct punctuation, just like
for your comments in Go.
If there is a helpful reference, mention it here.
If you've fixed an issue, reference it by number with a # before it.
</p>
<p>
@ -364,7 +367,7 @@ After editing, the template might now read:
</p>
<pre>
math: improved Sin, Cos and Tan precision for very large arguments
math: improve Sin, Cos and Tan precision for very large arguments
The existing implementation has poor numerical properties for
large arguments, so use the McGillicutty algorithm to improve

View file

@ -2014,7 +2014,7 @@ then make the receiver for the method a value of that type.
type ByteSlice []byte
func (slice ByteSlice) Append(data []byte) []byte {
// Body exactly the same as above
// Body exactly the same as the Append function defined above.
}
</pre>
<p>

1132
doc/go1.7.html Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
Tools:
cmd/dist: add list subcommand to list all supported platforms (CL 19837)
cmd/go: GO15VENDOREXPERIMENT gone, assumed on (CL 19615)
cmd/link: "-X name value" form gone (CL 19614)
cmd/compile: smaller binaries (many CLs)
cmd/go, go/build: add support for Fortran (CL 19670, CL 4114)
cmd/doc: group constructors with types (CL 22354)
cmd/go, go/build: binary-only package support (CL 22433)
Ports:
We now require OpenBSD 5.6+ (CL 18219, crypto/rand using getentropy)
plan9/arm support? Start at least.
cgo and external linking support for linux/mips64 and linux/mips64le (CL 19809, ...)
New packages:
* context (and new support in net, net/http, os/exec, net/http/httptrace)
* net/http/httptrace
API additions and behavior changes:
crypto/tls: allow renegotiation to be handled by a client (CL 22475)
runtime: support symbolic backtrace of C code in a cgo crash (CL 17761)
runtime: add CallerFrames and Frames (CL 19869)
testing/quick: now generates nil values (CL 16470)
net/http/httptest: ResponseRecorder supports trailer (CL 20047) (compat impact: issue 14928)
net/url: support query string without values (CL 19931)
net/textproto: permit all valid token chars in CanonicalMIMEHeaderKey input (CL 18725)
go/doc: add Unordered boolean to Example struct (CL 19280)
time: print zero duration as 0s, not 0 (CL 22357)

View file

@ -33,7 +33,7 @@ compiler using the GCC back end, see
</p>
<p>
The Go compilers support five instruction sets.
The Go compilers support six instruction sets.
There are important differences in the quality of the compilers for the different
architectures.
</p>
@ -63,19 +63,19 @@ architectures.
<code>arm64</code> (<code>AArch64</code>)
</dt>
<dd>
Supports Linux and Darwin binaries. New in 1.5 and not as well excercised as other ports.
Supports Linux and Darwin binaries. New in 1.5 and not as well exercised as other ports.
</dd>
<dt>
<code>ppc64, ppc64le</code> (64-bit PowerPC big- and little-endian)
</dt>
<dd>
Supports Linux binaries. New in 1.5 and not as well excercised as other ports.
Supports Linux binaries. New in 1.5 and not as well exercised as other ports.
</dd>
<dt>
<code>mips64, mips64le</code> (64-bit MIPS big- and little-endian)
</dt>
<dd>
Supports Linux binaries. New in 1.6 and not as well excercised as other ports.
Supports Linux binaries. New in 1.6 and not as well exercised as other ports.
</dd>
</dl>

View file

@ -221,7 +221,7 @@ and building a simple program, as follows.
<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">%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.
</p>
@ -231,7 +231,7 @@ $ <b>export GOPATH=$HOME/work</b>
</pre>
<pre class="testWindows" style="display: none">
C:\&gt; <b>set GOPATH=%HOME%\work</b>
C:\&gt; <b>set GOPATH=C:\work</b>
</pre>
<p>

View file

@ -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.
// Issue 14669: test that fails when build with CGO_CFLAGS selecting
// optimization.
package p
/*
const int E = 1;
typedef struct s {
int c;
} s;
*/
import "C"
func F() {
_ = C.s{
c: C.E,
}
}

View file

@ -290,6 +290,30 @@ var ptrTests = []ptrTest{
},
fail: true,
},
{
// Don't check non-pointer data.
// Uses unsafe code to get a pointer we shouldn't check.
// Although we use unsafe, the uintptr represents an integer
// that happens to have the same representation as a pointer;
// that is, we are testing something that is not unsafe.
name: "ptrdata1",
c: `#include <stdlib.h>
void f(void* p) {}`,
imports: []string{"unsafe"},
support: `type S struct { p *int; a [8*8]byte; u uintptr }`,
body: `i := 0; p := &S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f(unsafe.Pointer(q))`,
fail: false,
},
{
// Like ptrdata1, but with a type that uses a GC program.
name: "ptrdata2",
c: `#include <stdlib.h>
void f(void* p) {}`,
imports: []string{"unsafe"},
support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
body: `i := 0; p := S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f(unsafe.Pointer(q))`,
fail: false,
},
}
func main() {

View file

@ -45,6 +45,13 @@ expect issue13129.go C.ushort
check issue13423.go
expect issue13635.go C.uchar C.schar C.ushort C.uint C.ulong C.longlong C.ulonglong C.complexfloat C.complexdouble
if ! go build issue14669.go; then
exit 1
fi
if ! CGO_CFLAGS="-O" go build issue14669.go; then
exit 1
fi
if ! go run ptr.go; then
exit 1
fi

View file

@ -79,14 +79,12 @@ func init() {
}
if GOOS == "darwin" {
cc = append(cc, "-Wl,-no_pie")
// For Darwin/ARM.
// TODO(crawshaw): can we do better?
cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
}
libgodir = GOOS + "_" + GOARCH
if GOOS == "darwin" && GOARCH == "arm" {
if GOOS == "darwin" && (GOARCH == "arm" || GOARCH == "arm64") {
libgodir = GOOS + "_" + GOARCH + "_shared"
}
cc = append(cc, "-I", filepath.Join("pkg", libgodir))

View file

@ -44,8 +44,7 @@ static void init() {
// Test raising SIGIO on a C thread with an alternate signal stack
// when there is a Go signal handler for SIGIO.
static void* thread1(void* arg) {
pthread_t* ptid = (pthread_t*)(arg);
static void* thread1(void* arg __attribute__ ((unused))) {
stack_t ss;
int i;
stack_t nss;
@ -65,7 +64,7 @@ static void* thread1(void* arg) {
// Send ourselves a SIGIO. This will be caught by the Go
// signal handler which should forward to the C signal
// handler.
i = pthread_kill(*ptid, SIGIO);
i = pthread_kill(pthread_self(), SIGIO);
if (i != 0) {
fprintf(stderr, "pthread_kill: %s\n", strerror(i));
exit(EXIT_FAILURE);
@ -101,11 +100,11 @@ static void* thread1(void* arg) {
// Test calling a Go function to raise SIGIO on a C thread with an
// alternate signal stack when there is a Go signal handler for SIGIO.
static void* thread2(void* arg) {
pthread_t* ptid = (pthread_t*)(arg);
static void* thread2(void* arg __attribute__ ((unused))) {
stack_t ss;
int i;
int oldcount;
pthread_t tid;
stack_t nss;
// Set up an alternate signal stack for this thread.
@ -124,7 +123,8 @@ static void* thread2(void* arg) {
// Call a Go function that will call a C function to send us a
// SIGIO.
GoRaiseSIGIO(ptid);
tid = pthread_self();
GoRaiseSIGIO(&tid);
// Wait until the signal has been delivered.
i = 0;
@ -161,7 +161,7 @@ int main(int argc, char **argv) {
// Tell the Go library to start looking for SIGIO.
GoCatchSIGIO();
i = pthread_create(&tid, NULL, thread1, (void*)(&tid));
i = pthread_create(&tid, NULL, thread1, NULL);
if (i != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(i));
exit(EXIT_FAILURE);
@ -173,7 +173,7 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
i = pthread_create(&tid, NULL, thread2, (void*)(&tid));
i = pthread_create(&tid, NULL, thread2, NULL);
if (i != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(i));
exit(EXIT_FAILURE);

View file

@ -134,6 +134,26 @@ if test "$tsan" = "yes"; then
status=1
fi
if ! go run tsan3.go 2>$err; then
cat $err
echo "FAIL: tsan3"
status=1
elif grep -i warning $err >/dev/null 2>&1; then
cat $err
echo "FAIL: tsan3"
status=1
fi
if ! go run tsan4.go 2>$err; then
cat $err
echo "FAIL: tsan4"
status=1
elif grep -i warning $err >/dev/null 2>&1; then
cat $err
echo "FAIL: tsan4"
status=1
fi
rm -f $err
fi

View file

@ -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
// The stubs for the C functions read and write the same slot on the
// g0 stack when copying arguments in and out.
/*
#cgo CFLAGS: -fsanitize=thread
#cgo LDFLAGS: -fsanitize=thread
int Func1() {
return 0;
}
void Func2(int x) {
(void)x;
}
*/
import "C"
func main() {
const N = 10000
done := make(chan bool, N)
for i := 0; i < N; i++ {
go func() {
C.Func1()
done <- true
}()
go func() {
C.Func2(0)
done <- true
}()
}
for i := 0; i < 2*N; i++ {
<-done
}
}

View file

@ -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.
package main
// Check that calls to C.malloc/C.free do not trigger TSAN false
// positive reports.
// #cgo CFLAGS: -fsanitize=thread
// #cgo LDFLAGS: -fsanitize=thread
// #include <stdlib.h>
import "C"
import (
"runtime"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
p := C.malloc(C.size_t(i * 10))
runtime.Gosched()
C.free(p)
}
}()
}
wg.Wait()
}

View file

@ -27,7 +27,12 @@ go src=..
internal
objfile
objfile.go
unvendor
gofmt
gofmt.go
gofmt_test.go
testdata
+
vendor
golang.org
x
arch
@ -39,11 +44,6 @@ go src=..
x86asm
testdata
+
gofmt
gofmt.go
gofmt_test.go
testdata
+
archive
tar
testdata

View file

@ -21,10 +21,8 @@ import (
"time"
)
// Header type flags.
const (
blockSize = 512
// Types
TypeReg = '0' // regular file
TypeRegA = '\x00' // regular file
TypeLink = '1' // hard link
@ -61,12 +59,6 @@ type Header struct {
Xattrs map[string]string
}
// File name constants from the tar spec.
const (
fileNameSize = 100 // Maximum number of bytes in a standard tar name.
fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
)
// FileInfo returns an os.FileInfo for the Header.
func (h *Header) FileInfo() os.FileInfo {
return headerFileInfo{h}
@ -279,33 +271,6 @@ func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
return h, nil
}
var zeroBlock = make([]byte, blockSize)
// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
// We compute and return both.
func checksum(header []byte) (unsigned int64, signed int64) {
for i := 0; i < len(header); i++ {
if i == 148 {
// The chksum field (header[148:156]) is special: it should be treated as space bytes.
unsigned += ' ' * 8
signed += ' ' * 8
i += 7
continue
}
unsigned += int64(header[i])
signed += int64(int8(header[i]))
}
return
}
type slicer []byte
func (sp *slicer) next(n int) (b []byte) {
s := *sp
b, *sp = s[0:n], s[n:]
return
}
func isASCII(s string) bool {
for _, c := range s {
if c >= 0x80 {

197
src/archive/tar/format.go Normal file
View file

@ -0,0 +1,197 @@
// Copyright 2016 The Go Authors. 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
// Constants to identify various tar formats.
const (
// The format is unknown.
formatUnknown = (1 << iota) / 2 // Sequence of 0, 1, 2, 4, 8, etc...
// The format of the original Unix V7 tar tool prior to standardization.
formatV7
// The old and new GNU formats, which are incompatible with USTAR.
// This does cover the old GNU sparse extension.
// This does not cover the GNU sparse extensions using PAX headers,
// versions 0.0, 0.1, and 1.0; these fall under the PAX format.
formatGNU
// Schily's tar format, which is incompatible with USTAR.
// This does not cover STAR extensions to the PAX format; these fall under
// the PAX format.
formatSTAR
// USTAR is the former standardization of tar defined in POSIX.1-1988.
// This is incompatible with the GNU and STAR formats.
formatUSTAR
// PAX is the latest standardization of tar defined in POSIX.1-2001.
// This is an extension of USTAR and is "backwards compatible" with it.
//
// Some newer formats add their own extensions to PAX, such as GNU sparse
// files and SCHILY extended attributes. Since they are backwards compatible
// with PAX, they will be labelled as "PAX".
formatPAX
)
// Magics used to identify various formats.
const (
magicGNU, versionGNU = "ustar ", " \x00"
magicUSTAR, versionUSTAR = "ustar\x00", "00"
trailerSTAR = "tar\x00"
)
// Size constants from various tar specifications.
const (
blockSize = 512 // Size of each block in a tar stream
nameSize = 100 // Max length of the name field in USTAR format
prefixSize = 155 // Max length of the prefix field in USTAR format
)
var zeroBlock block
type block [blockSize]byte
// Convert block to any number of formats.
func (b *block) V7() *headerV7 { return (*headerV7)(b) }
func (b *block) GNU() *headerGNU { return (*headerGNU)(b) }
func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) }
func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) }
func (b *block) Sparse() sparseArray { return (sparseArray)(b[:]) }
// GetFormat checks that the block is a valid tar header based on the checksum.
// It then attempts to guess the specific format based on magic values.
// If the checksum fails, then formatUnknown is returned.
func (b *block) GetFormat() (format int) {
// Verify checksum.
var p parser
value := p.parseOctal(b.V7().Chksum())
chksum1, chksum2 := b.ComputeChecksum()
if p.err != nil || (value != chksum1 && value != chksum2) {
return formatUnknown
}
// Guess the magic values.
magic := string(b.USTAR().Magic())
version := string(b.USTAR().Version())
trailer := string(b.STAR().Trailer())
switch {
case magic == magicUSTAR && trailer == trailerSTAR:
return formatSTAR
case magic == magicUSTAR:
return formatUSTAR
case magic == magicGNU && version == versionGNU:
return formatGNU
default:
return formatV7
}
}
// SetFormat writes the magic values necessary for specified format
// and then updates the checksum accordingly.
func (b *block) SetFormat(format int) {
// Set the magic values.
switch format {
case formatV7:
// Do nothing.
case formatGNU:
copy(b.GNU().Magic(), magicGNU)
copy(b.GNU().Version(), versionGNU)
case formatSTAR:
copy(b.STAR().Magic(), magicUSTAR)
copy(b.STAR().Version(), versionUSTAR)
copy(b.STAR().Trailer(), trailerSTAR)
case formatUSTAR, formatPAX:
copy(b.USTAR().Magic(), magicUSTAR)
copy(b.USTAR().Version(), versionUSTAR)
default:
panic("invalid format")
}
// Update checksum.
// This field is special in that it is terminated by a NULL then space.
var f formatter
field := b.V7().Chksum()
chksum, _ := b.ComputeChecksum() // Possible values are 256..128776
f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143
field[7] = ' '
}
// ComputeChecksum computes the checksum for the header block.
// POSIX specifies a sum of the unsigned byte values, but the Sun tar used
// signed byte values.
// We compute and return both.
func (b *block) ComputeChecksum() (unsigned, signed int64) {
for i, c := range b {
if 148 <= i && i < 156 {
c = ' ' // Treat the checksum field itself as all spaces.
}
unsigned += int64(uint8(c))
signed += int64(int8(c))
}
return unsigned, signed
}
type headerV7 [blockSize]byte
func (h *headerV7) Name() []byte { return h[000:][:100] }
func (h *headerV7) Mode() []byte { return h[100:][:8] }
func (h *headerV7) UID() []byte { return h[108:][:8] }
func (h *headerV7) GID() []byte { return h[116:][:8] }
func (h *headerV7) Size() []byte { return h[124:][:12] }
func (h *headerV7) ModTime() []byte { return h[136:][:12] }
func (h *headerV7) Chksum() []byte { return h[148:][:8] }
func (h *headerV7) TypeFlag() []byte { return h[156:][:1] }
func (h *headerV7) LinkName() []byte { return h[157:][:100] }
type headerGNU [blockSize]byte
func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) }
func (h *headerGNU) Magic() []byte { return h[257:][:6] }
func (h *headerGNU) Version() []byte { return h[263:][:2] }
func (h *headerGNU) UserName() []byte { return h[265:][:32] }
func (h *headerGNU) GroupName() []byte { return h[297:][:32] }
func (h *headerGNU) DevMajor() []byte { return h[329:][:8] }
func (h *headerGNU) DevMinor() []byte { return h[337:][:8] }
func (h *headerGNU) AccessTime() []byte { return h[345:][:12] }
func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] }
func (h *headerGNU) Sparse() sparseArray { return (sparseArray)(h[386:][:24*4+1]) }
func (h *headerGNU) RealSize() []byte { return h[483:][:12] }
type headerSTAR [blockSize]byte
func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) }
func (h *headerSTAR) Magic() []byte { return h[257:][:6] }
func (h *headerSTAR) Version() []byte { return h[263:][:2] }
func (h *headerSTAR) UserName() []byte { return h[265:][:32] }
func (h *headerSTAR) GroupName() []byte { return h[297:][:32] }
func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] }
func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] }
func (h *headerSTAR) Prefix() []byte { return h[345:][:131] }
func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] }
func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] }
func (h *headerSTAR) Trailer() []byte { return h[508:][:4] }
type headerUSTAR [blockSize]byte
func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) }
func (h *headerUSTAR) Magic() []byte { return h[257:][:6] }
func (h *headerUSTAR) Version() []byte { return h[263:][:2] }
func (h *headerUSTAR) UserName() []byte { return h[265:][:32] }
func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] }
func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] }
func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] }
func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] }
type sparseArray []byte
func (s sparseArray) Entry(i int) sparseNode { return (sparseNode)(s[i*24:]) }
func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] }
func (s sparseArray) MaxEntries() int { return len(s) / 24 }
type sparseNode []byte
func (s sparseNode) Offset() []byte { return s[00:][:12] }
func (s sparseNode) NumBytes() []byte { return s[12:][:12] }

View file

@ -33,7 +33,7 @@ type Reader struct {
err error
pad int64 // amount of padding (ignored) after current file entry
curr numBytesReader // reader for current file entry
hdrBuff [blockSize]byte // buffer to use in readHeader
blk block // buffer to use as temporary local storage
}
type parser struct {
@ -98,17 +98,6 @@ const (
paxGNUSparseRealSize = "GNU.sparse.realsize"
)
// Keywords for old GNU sparse headers
const (
oldGNUSparseMainHeaderOffset = 386
oldGNUSparseMainHeaderIsExtendedOffset = 482
oldGNUSparseMainHeaderNumEntries = 4
oldGNUSparseExtendedHeaderIsExtendedOffset = 504
oldGNUSparseExtendedHeaderNumEntries = 21
oldGNUSparseOffsetSize = 12
oldGNUSparseNumBytesSize = 12
)
// NewReader creates a new Reader reading from r.
func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
@ -542,17 +531,6 @@ func (tr *Reader) skipUnread() error {
return tr.err
}
func (tr *Reader) verifyChecksum(header []byte) bool {
if tr.err != nil {
return false
}
var p parser
given := p.parseOctal(header[148:156])
unsigned, signed := checksum(header)
return p.err == nil && (given == unsigned || given == signed)
}
// readHeader reads the next block header and assumes that the underlying reader
// is already aligned to a block boundary.
//
@ -561,19 +539,16 @@ func (tr *Reader) verifyChecksum(header []byte) bool {
// * Exactly 1 block of zeros is read and EOF is hit.
// * At least 2 blocks of zeros are read.
func (tr *Reader) readHeader() *Header {
header := tr.hdrBuff[:]
copy(header, zeroBlock)
if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
if _, tr.err = io.ReadFull(tr.r, tr.blk[:]); tr.err != nil {
return nil // io.EOF is okay here
}
// Two blocks of zero bytes marks the end of the archive.
if bytes.Equal(header, zeroBlock[0:blockSize]) {
if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
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 bytes.Equal(header, zeroBlock[0:blockSize]) {
if bytes.Equal(tr.blk[:], zeroBlock[:]) {
tr.err = io.EOF
} else {
tr.err = ErrHeader // zero block and then non-zero block
@ -581,71 +556,55 @@ func (tr *Reader) readHeader() *Header {
return nil
}
if !tr.verifyChecksum(header) {
// Verify the header matches a known format.
format := tr.blk.GetFormat()
if format == formatUnknown {
tr.err = ErrHeader
return nil
}
// Unpack
var p parser
hdr := new(Header)
s := slicer(header)
hdr.Name = p.parseString(s.next(100))
hdr.Mode = p.parseNumeric(s.next(8))
hdr.Uid = int(p.parseNumeric(s.next(8)))
hdr.Gid = int(p.parseNumeric(s.next(8)))
hdr.Size = p.parseNumeric(s.next(12))
hdr.ModTime = time.Unix(p.parseNumeric(s.next(12)), 0)
s.next(8) // chksum
hdr.Typeflag = s.next(1)[0]
hdr.Linkname = p.parseString(s.next(100))
// Unpack the V7 header.
v7 := tr.blk.V7()
hdr.Name = p.parseString(v7.Name())
hdr.Mode = p.parseNumeric(v7.Mode())
hdr.Uid = int(p.parseNumeric(v7.UID()))
hdr.Gid = int(p.parseNumeric(v7.GID()))
hdr.Size = p.parseNumeric(v7.Size())
hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0)
hdr.Typeflag = v7.TypeFlag()[0]
hdr.Linkname = p.parseString(v7.LinkName())
// The remainder of the header depends on the value of magic.
// The original (v7) version of tar had no explicit magic field,
// so its magic bytes, like the rest of the block, are NULs.
magic := string(s.next(8)) // contains version field as well.
var format string
switch {
case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988)
if string(header[508:512]) == "tar\x00" {
format = "star"
} else {
format = "posix"
}
case magic == "ustar \x00": // old GNU tar
format = "gnu"
}
switch format {
case "posix", "gnu", "star":
hdr.Uname = p.parseString(s.next(32))
hdr.Gname = p.parseString(s.next(32))
devmajor := s.next(8)
devminor := s.next(8)
// Unpack format specific fields.
if format > formatV7 {
ustar := tr.blk.USTAR()
hdr.Uname = p.parseString(ustar.UserName())
hdr.Gname = p.parseString(ustar.GroupName())
if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
hdr.Devmajor = p.parseNumeric(devmajor)
hdr.Devminor = p.parseNumeric(devminor)
hdr.Devmajor = p.parseNumeric(ustar.DevMajor())
hdr.Devminor = p.parseNumeric(ustar.DevMinor())
}
var prefix string
switch format {
case "posix", "gnu":
prefix = p.parseString(s.next(155))
case "star":
prefix = p.parseString(s.next(131))
hdr.AccessTime = time.Unix(p.parseNumeric(s.next(12)), 0)
hdr.ChangeTime = time.Unix(p.parseNumeric(s.next(12)), 0)
case formatUSTAR, formatGNU:
// TODO(dsnet): Do not use the prefix field for the GNU format!
// See golang.org/issues/12594
ustar := tr.blk.USTAR()
prefix = p.parseString(ustar.Prefix())
case formatSTAR:
star := tr.blk.STAR()
prefix = p.parseString(star.Prefix())
hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0)
hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0)
}
if len(prefix) > 0 {
hdr.Name = prefix + "/" + hdr.Name
}
}
if p.err != nil {
tr.err = p.err
return nil
}
nb := hdr.Size
if isHeaderOnlyType(hdr.Typeflag) {
nb = 0
@ -662,14 +621,14 @@ func (tr *Reader) readHeader() *Header {
// Check for old GNU sparse format entry.
if hdr.Typeflag == TypeGNUSparse {
// Get the real size of the file.
hdr.Size = p.parseNumeric(header[483:495])
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(header)
sp := tr.readOldGNUSparseMap(&tr.blk)
if tr.err != nil {
return nil
}
@ -681,26 +640,24 @@ func (tr *Reader) readHeader() *Header {
}
}
if p.err != nil {
tr.err = p.err
return nil
}
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(header []byte) []sparseEntry {
func (tr *Reader) readOldGNUSparseMap(blk *block) []sparseEntry {
var p parser
isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0
spCap := oldGNUSparseMainHeaderNumEntries
if isExtended {
spCap += oldGNUSparseExtendedHeaderNumEntries
}
sp := make([]sparseEntry, 0, spCap)
s := slicer(header[oldGNUSparseMainHeaderOffset:])
// Read the four entries from the main tar header
for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ {
offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
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
@ -711,17 +668,17 @@ func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
}
for isExtended {
for s.IsExtended()[0] > 0 {
// There are more entries. Read an extension header and parse its entries.
sparseHeader := make([]byte, blockSize)
if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil {
var blk block
if _, tr.err = io.ReadFull(tr.r, blk[:]); tr.err != nil {
return nil
}
isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0
s = slicer(sparseHeader)
for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ {
offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
s = blk.Sparse()
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

View file

@ -37,9 +37,9 @@ type Writer struct {
pad int64 // amount of padding to write after current file entry
closed bool
usedBinary bool // whether the binary numeric field extension was used
preferPax bool // use pax header instead of binary numeric header
hdrBuff [blockSize]byte // buffer to use in writeHeader when writing a regular header
paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header
preferPax bool // use PAX header instead of binary numeric header
hdrBuff block // buffer to use in writeHeader when writing a regular header
paxHdrBuff block // buffer to use in writeHeader when writing a PAX header
}
type formatter struct {
@ -153,27 +153,24 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
// a map to hold pax header records, if any are needed
paxHeaders := make(map[string]string)
// TODO(shanemhansen): we might want to use PAX headers for
// TODO(dsnet): we might want to use PAX headers for
// subsecond time resolution, but for now let's just capture
// too long fields or non ascii characters
var f formatter
var header []byte
// We need to select which scratch buffer to use carefully,
// since this method is called recursively to write PAX headers.
// If allowPax is true, this is the non-recursive call, and we will use hdrBuff.
// If allowPax is false, we are being called by writePAXHeader, and hdrBuff is
// already being used by the non-recursive call, so we must use paxHdrBuff.
header = tw.hdrBuff[:]
header := &tw.hdrBuff
if !allowPax {
header = tw.paxHdrBuff[:]
header = &tw.paxHdrBuff
}
copy(header, zeroBlock)
s := slicer(header)
copy(header[:], zeroBlock[:])
// Wrappers around formatter that automatically sets paxHeaders if the
// argument extends beyond the capacity of the input byte slice.
var f formatter
var formatString = func(b []byte, s string, paxKeyword string) {
needsPaxHeader := paxKeyword != paxNone && len(s) > len(b) || !isASCII(s)
if needsPaxHeader {
@ -202,44 +199,33 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
f.formatNumeric(b, x)
}
// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
pathHeaderBytes := s.next(fileNameSize)
formatString(pathHeaderBytes, hdr.Name, paxPath)
// Handle out of range ModTime carefully.
var modTime int64
if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
modTime = hdr.ModTime.Unix()
}
f.formatOctal(s.next(8), hdr.Mode) // 100:108
formatNumeric(s.next(8), int64(hdr.Uid), paxUid) // 108:116
formatNumeric(s.next(8), int64(hdr.Gid), paxGid) // 116:124
formatNumeric(s.next(12), hdr.Size, paxSize) // 124:136
formatNumeric(s.next(12), modTime, paxNone) // 136:148 --- consider using pax for finer granularity
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157
v7 := header.V7()
formatString(v7.Name(), hdr.Name, paxPath)
// TODO(dsnet): The GNU format permits the mode field to be encoded in
// base-256 format. Thus, we can use formatNumeric instead of formatOctal.
f.formatOctal(v7.Mode(), hdr.Mode)
formatNumeric(v7.UID(), int64(hdr.Uid), paxUid)
formatNumeric(v7.GID(), int64(hdr.Gid), paxGid)
formatNumeric(v7.Size(), hdr.Size, paxSize)
// TODO(dsnet): Consider using PAX for finer time granularity.
formatNumeric(v7.ModTime(), modTime, paxNone)
v7.TypeFlag()[0] = hdr.Typeflag
formatString(v7.LinkName(), hdr.Linkname, paxLinkpath)
formatString(s.next(100), hdr.Linkname, paxLinkpath)
ustar := header.USTAR()
formatString(ustar.UserName(), hdr.Uname, paxUname)
formatString(ustar.GroupName(), hdr.Gname, paxGname)
formatNumeric(ustar.DevMajor(), hdr.Devmajor, paxNone)
formatNumeric(ustar.DevMinor(), hdr.Devminor, paxNone)
copy(s.next(8), []byte("ustar\x0000")) // 257:265
formatString(s.next(32), hdr.Uname, paxUname) // 265:297
formatString(s.next(32), hdr.Gname, paxGname) // 297:329
formatNumeric(s.next(8), hdr.Devmajor, paxNone) // 329:337
formatNumeric(s.next(8), hdr.Devminor, paxNone) // 337:345
// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
prefixHeaderBytes := s.next(155)
formatString(prefixHeaderBytes, "", paxNone) // 345:500 prefix
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary {
copy(header[257:265], []byte("ustar \x00"))
}
_, paxPathUsed := paxHeaders[paxPath]
// try to use a ustar header when only the name is too long
_, paxPathUsed := paxHeaders[paxPath]
if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
prefix, suffix, ok := splitUSTARPath(hdr.Name)
if ok {
@ -247,16 +233,16 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
delete(paxHeaders, paxPath)
// Update the path fields
formatString(pathHeaderBytes, suffix, paxNone)
formatString(prefixHeaderBytes, prefix, paxNone)
formatString(v7.Name(), suffix, paxNone)
formatString(ustar.Prefix(), prefix, paxNone)
}
}
// The chksum field is terminated by a NUL and a space.
// This is different from the other octal fields.
chksum, _ := checksum(header)
f.formatOctal(header[148:155], chksum) // Never fails
header[155] = ' '
if tw.usedBinary {
header.SetFormat(formatGNU)
} else {
header.SetFormat(formatUSTAR)
}
// Check if there were any formatting errors.
if f.err != nil {
@ -281,7 +267,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
tw.nb = hdr.Size
tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
_, tw.err = tw.w.Write(header)
_, tw.err = tw.w.Write(header[:])
return tw.err
}
@ -289,10 +275,10 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
// If the path is not splittable, then it will return ("", "", false).
func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
length := len(name)
if length <= fileNameSize || !isASCII(name) {
if length <= nameSize || !isASCII(name) {
return "", "", false
} else if length > fileNamePrefixSize+1 {
length = fileNamePrefixSize + 1
} else if length > prefixSize+1 {
length = prefixSize + 1
} else if name[length-1] == '/' {
length--
}
@ -300,7 +286,7 @@ func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
i := strings.LastIndex(name[:length], "/")
nlen := len(name) - i - 1 // nlen is length of suffix
plen := i // plen is length of prefix
if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize {
return "", "", false
}
return name[:i], name[i+1:], true
@ -323,8 +309,8 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
fullName := path.Join(dir, "PaxHeaders.0", file)
ascii := toASCII(fullName)
if len(ascii) > 100 {
ascii = ascii[:100]
if len(ascii) > nameSize {
ascii = ascii[:nameSize]
}
ext.Name = ascii
// Construct the body
@ -407,7 +393,7 @@ func (tw *Writer) Close() error {
// trailer: two zero blocks
for i := 0; i < 2; i++ {
_, tw.err = tw.w.Write(zeroBlock)
_, tw.err = tw.w.Write(zeroBlock[:])
if tw.err != nil {
break
}

View file

@ -587,17 +587,17 @@ func TestSplitUSTARPath(t *testing.T) {
{"", "", "", false},
{"abc", "", "", false},
{"用戶名", "", "", false},
{sr("a", fileNameSize), "", "", false},
{sr("a", fileNameSize) + "/", "", "", false},
{sr("a", fileNameSize) + "/a", sr("a", fileNameSize), "a", true},
{sr("a", fileNamePrefixSize) + "/", "", "", false},
{sr("a", fileNamePrefixSize) + "/a", sr("a", fileNamePrefixSize), "a", true},
{sr("a", fileNameSize+1), "", "", false},
{sr("/", fileNameSize+1), sr("/", fileNameSize-1), "/", true},
{sr("a", fileNamePrefixSize) + "/" + sr("b", fileNameSize),
sr("a", fileNamePrefixSize), sr("b", fileNameSize), true},
{sr("a", fileNamePrefixSize) + "//" + sr("b", fileNameSize), "", "", false},
{sr("a/", fileNameSize), sr("a/", 77) + "a", sr("a/", 22), true},
{sr("a", nameSize), "", "", false},
{sr("a", nameSize) + "/", "", "", false},
{sr("a", nameSize) + "/a", sr("a", nameSize), "a", true},
{sr("a", prefixSize) + "/", "", "", false},
{sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true},
{sr("a", nameSize+1), "", "", false},
{sr("/", nameSize+1), sr("/", nameSize-1), "/", true},
{sr("a", prefixSize) + "/" + sr("b", nameSize),
sr("a", prefixSize), sr("b", nameSize), true},
{sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false},
{sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true},
}
for _, v := range vectors {

View file

@ -5,7 +5,7 @@
/*
Package zip provides support for reading and writing ZIP archives.
See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
See: https://www.pkware.com/documents/casestudies/APPNOTE.TXT
This package does not support disk spanning.

View file

@ -1475,7 +1475,7 @@ func BenchmarkReaderWriteToOptimal(b *testing.B) {
b.Fatal("ioutil.Discard doesn't support ReaderFrom")
}
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
r.Seek(0, io.SeekStart)
srcReader.Reset(onlyReader{r})
n, err := srcReader.WriteTo(ioutil.Discard)
if err != nil {

View file

@ -108,11 +108,11 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
r.prevRune = -1
var abs int64
switch whence {
case 0:
case io.SeekStart:
abs = offset
case 1:
case io.SeekCurrent:
abs = r.i + offset
case 2:
case io.SeekEnd:
abs = int64(len(r.s)) + offset
default:
return 0, errors.New("bytes.Reader.Seek: invalid whence")

View file

@ -188,7 +188,7 @@ var UnreadRuneErrorTests = []struct {
{"Read", func(r *Reader) { r.Read([]byte{0}) }},
{"ReadByte", func(r *Reader) { r.ReadByte() }},
{"UnreadRune", func(r *Reader) { r.UnreadRune() }},
{"Seek", func(r *Reader) { r.Seek(0, 1) }},
{"Seek", func(r *Reader) { r.Seek(0, io.SeekCurrent) }},
{"WriteTo", func(r *Reader) { r.WriteTo(&Buffer{}) }},
}

View file

@ -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.
// This file encapsulates some of the odd characteristics of the
// AMD64 instruction set, to minimize its interaction
// with the core of the assembler.
package arch
import (
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
// IsAMD4OP reports whether the op (as defined by an ppc64.A* constant) is
// The FMADD-like instructions behave similarly.
func IsAMD4OP(op obj.As) bool {
switch op {
case x86.AVPERM2F128,
x86.AVPALIGNR,
x86.AVPERM2I128,
x86.AVINSERTI128,
x86.AVPBLENDD:
return true
}
return false
}

View file

@ -568,6 +568,15 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
case sys.AMD64:
// 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))
}
prog.From = a[0]
prog.From3 = newAddr(a[1])
prog.To = a[2]
case sys.ARM64:
// ARM64 instructions with one input and two outputs.
if arch.IsARM64STLXR(op) {
@ -583,7 +592,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
case sys.AMD64, sys.I386:
case sys.I386:
prog.From = a[0]
prog.From3 = newAddr(a[1])
prog.To = a[2]
@ -640,6 +649,23 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.Reg = r1
break
}
if p.arch.Family == sys.AMD64 {
// 4 operand instruction have form ymm1, ymm2, ymm3/m256, imm8
// So From3 is always just a register, so we store imm8 in Offset field,
// to avoid increasing size of Prog.
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))
}
if prog.From3.Type != obj.TYPE_REG {
p.errorf("third operand must be a register in %s instruction", obj.Aconv(op))
}
prog.From3.Offset = int64(p.getImmediate(prog, op, &a[0]))
prog.To = a[3]
prog.RegTo2 = -1
break
}
if p.arch.Family == sys.ARM64 {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])

View file

@ -5008,22 +5008,22 @@ TEXT asmtest(SB),7,$0
RORB $7, (R11) // 41c00b07
RORB $7, DL // c0ca07
RORB $7, R11 // 41c0cb07
//TODO: RORXL $7, (BX), DX // c4e37bf01307
//TODO: RORXL $7, (R11), DX // c4c37bf01307
//TODO: RORXL $7, DX, DX // c4e37bf0d207
//TODO: RORXL $7, R11, DX // c4c37bf0d307
//TODO: RORXL $7, (BX), R11 // c4637bf01b07
//TODO: RORXL $7, (R11), R11 // c4437bf01b07
//TODO: RORXL $7, DX, R11 // c4637bf0da07
//TODO: RORXL $7, R11, R11 // c4437bf0db07
//TODO: RORXQ $7, (BX), DX // c4e3fbf01307
//TODO: RORXQ $7, (R11), DX // c4c3fbf01307
//TODO: RORXQ $7, DX, DX // c4e3fbf0d207
//TODO: RORXQ $7, R11, DX // c4c3fbf0d307
//TODO: RORXQ $7, (BX), R11 // c463fbf01b07
//TODO: RORXQ $7, (R11), R11 // c443fbf01b07
//TODO: RORXQ $7, DX, R11 // c463fbf0da07
//TODO: RORXQ $7, R11, R11 // c443fbf0db07
RORXL $7, (BX), DX // c4e37bf01307
RORXL $7, (R11), DX // c4c37bf01307
RORXL $7, DX, DX // c4e37bf0d207
RORXL $7, R11, DX // c4c37bf0d307
RORXL $7, (BX), R11 // c4637bf01b07
RORXL $7, (R11), R11 // c4437bf01b07
RORXL $7, DX, R11 // c4637bf0da07
RORXL $7, R11, R11 // c4437bf0db07
RORXQ $7, (BX), DX // c4e3fbf01307
RORXQ $7, (R11), DX // c4c3fbf01307
RORXQ $7, DX, DX // c4e3fbf0d207
RORXQ $7, R11, DX // c4c3fbf0d307
RORXQ $7, (BX), R11 // c463fbf01b07
RORXQ $7, (R11), R11 // c443fbf01b07
RORXQ $7, DX, R11 // c463fbf0da07
RORXQ $7, R11, R11 // c443fbf0db07
ROUNDPD $7, (BX), X2 // 660f3a091307
ROUNDPD $7, (R11), X2 // 66410f3a091307
ROUNDPD $7, X2, X2 // 660f3a09d207
@ -7420,14 +7420,14 @@ TEXT asmtest(SB),7,$0
//TODO: VINSERTF128 $7, (R11), Y15, Y11 // c44305181b07
//TODO: VINSERTF128 $7, X2, Y15, Y11 // c4630518da07
//TODO: VINSERTF128 $7, X11, Y15, Y11 // c4430518db07
//TODO: VINSERTI128 $7, (BX), Y15, Y2 // c4e305381307
//TODO: VINSERTI128 $7, (R11), Y15, Y2 // c4c305381307
//TODO: VINSERTI128 $7, X2, Y15, Y2 // c4e30538d207
//TODO: VINSERTI128 $7, X11, Y15, Y2 // c4c30538d307
//TODO: VINSERTI128 $7, (BX), Y15, Y11 // c46305381b07
//TODO: VINSERTI128 $7, (R11), Y15, Y11 // c44305381b07
//TODO: VINSERTI128 $7, X2, Y15, Y11 // c4630538da07
//TODO: VINSERTI128 $7, X11, Y15, Y11 // c4430538db07
VINSERTI128 $7, (BX), Y15, Y2 // c4e305381307
VINSERTI128 $7, (R11), Y15, Y2 // c4c305381307
VINSERTI128 $7, X2, Y15, Y2 // c4e30538d207
VINSERTI128 $7, X11, Y15, Y2 // c4c30538d307
VINSERTI128 $7, (BX), Y15, Y11 // c46305381b07
VINSERTI128 $7, (R11), Y15, Y11 // c44305381b07
VINSERTI128 $7, X2, Y15, Y11 // c4630538da07
VINSERTI128 $7, X11, Y15, Y11 // c4430538db07
//TODO: VINSERTPS $7, (BX), X9, X2 // c4e331211307
//TODO: VINSERTPS $7, (R11), X9, X2 // c4c331211307
//TODO: VINSERTPS $7, X2, X9, X2 // c4e33121d207
@ -8142,38 +8142,38 @@ TEXT asmtest(SB),7,$0
//TODO: VPADDB (R11), Y15, Y11 // c44105fc1b
//TODO: VPADDB Y2, Y15, Y11 // c46105fcda or c505fcda
//TODO: VPADDB Y11, Y15, Y11 // c44105fcdb
//TODO: VPADDD (BX), X9, X2 // c4e131fe13 or c5b1fe13
//TODO: VPADDD (R11), X9, X2 // c4c131fe13
//TODO: VPADDD X2, X9, X2 // c4e131fed2 or c5b1fed2
//TODO: VPADDD X11, X9, X2 // c4c131fed3
//TODO: VPADDD (BX), X9, X11 // c46131fe1b or c531fe1b
//TODO: VPADDD (R11), X9, X11 // c44131fe1b
//TODO: VPADDD X2, X9, X11 // c46131feda or c531feda
//TODO: VPADDD X11, X9, X11 // c44131fedb
//TODO: VPADDD (BX), Y15, Y2 // c4e105fe13 or c585fe13
//TODO: VPADDD (R11), Y15, Y2 // c4c105fe13
//TODO: VPADDD Y2, Y15, Y2 // c4e105fed2 or c585fed2
//TODO: VPADDD Y11, Y15, Y2 // c4c105fed3
//TODO: VPADDD (BX), Y15, Y11 // c46105fe1b or c505fe1b
//TODO: VPADDD (R11), Y15, Y11 // c44105fe1b
//TODO: VPADDD Y2, Y15, Y11 // c46105feda or c505feda
//TODO: VPADDD Y11, Y15, Y11 // c44105fedb
//TODO: VPADDQ (BX), X9, X2 // c4e131d413 or c5b1d413
//TODO: VPADDQ (R11), X9, X2 // c4c131d413
//TODO: VPADDQ X2, X9, X2 // c4e131d4d2 or c5b1d4d2
//TODO: VPADDQ X11, X9, X2 // c4c131d4d3
//TODO: VPADDQ (BX), X9, X11 // c46131d41b or c531d41b
//TODO: VPADDQ (R11), X9, X11 // c44131d41b
//TODO: VPADDQ X2, X9, X11 // c46131d4da or c531d4da
//TODO: VPADDQ X11, X9, X11 // c44131d4db
//TODO: VPADDQ (BX), Y15, Y2 // c4e105d413 or c585d413
//TODO: VPADDQ (R11), Y15, Y2 // c4c105d413
//TODO: VPADDQ Y2, Y15, Y2 // c4e105d4d2 or c585d4d2
//TODO: VPADDQ Y11, Y15, Y2 // c4c105d4d3
//TODO: VPADDQ (BX), Y15, Y11 // c46105d41b or c505d41b
//TODO: VPADDQ (R11), Y15, Y11 // c44105d41b
//TODO: VPADDQ Y2, Y15, Y11 // c46105d4da or c505d4da
//TODO: VPADDQ Y11, Y15, Y11 // c44105d4db
VPADDD (BX), X9, X2 // c4e131fe13 or c5b1fe13
VPADDD (R11), X9, X2 // c4c131fe13
VPADDD X2, X9, X2 // c4e131fed2 or c5b1fed2
VPADDD X11, X9, X2 // c4c131fed3
VPADDD (BX), X9, X11 // c46131fe1b or c531fe1b
VPADDD (R11), X9, X11 // c44131fe1b
VPADDD X2, X9, X11 // c46131feda or c531feda
VPADDD X11, X9, X11 // c44131fedb
VPADDD (BX), Y15, Y2 // c4e105fe13 or c585fe13
VPADDD (R11), Y15, Y2 // c4c105fe13
VPADDD Y2, Y15, Y2 // c4e105fed2 or c585fed2
VPADDD Y11, Y15, Y2 // c4c105fed3
VPADDD (BX), Y15, Y11 // c46105fe1b or c505fe1b
VPADDD (R11), Y15, Y11 // c44105fe1b
VPADDD Y2, Y15, Y11 // c46105feda or c505feda
VPADDD Y11, Y15, Y11 // c44105fedb
VPADDQ (BX), X9, X2 // c4e131d413 or c5b1d413
VPADDQ (R11), X9, X2 // c4c131d413
VPADDQ X2, X9, X2 // c4e131d4d2 or c5b1d4d2
VPADDQ X11, X9, X2 // c4c131d4d3
VPADDQ (BX), X9, X11 // c46131d41b or c531d41b
VPADDQ (R11), X9, X11 // c44131d41b
VPADDQ X2, X9, X11 // c46131d4da or c531d4da
VPADDQ X11, X9, X11 // c44131d4db
VPADDQ (BX), Y15, Y2 // c4e105d413 or c585d413
VPADDQ (R11), Y15, Y2 // c4c105d413
VPADDQ Y2, Y15, Y2 // c4e105d4d2 or c585d4d2
VPADDQ Y11, Y15, Y2 // c4c105d4d3
VPADDQ (BX), Y15, Y11 // c46105d41b or c505d41b
VPADDQ (R11), Y15, Y11 // c44105d41b
VPADDQ Y2, Y15, Y11 // c46105d4da or c505d4da
VPADDQ Y11, Y15, Y11 // c44105d4db
//TODO: VPADDSB (BX), X9, X2 // c4e131ec13 or c5b1ec13
//TODO: VPADDSB (R11), X9, X2 // c4c131ec13
//TODO: VPADDSB X2, X9, X2 // c4e131ecd2 or c5b1ecd2
@ -8262,14 +8262,14 @@ TEXT asmtest(SB),7,$0
//TODO: VPALIGNR $7, (R11), X9, X11 // c443310f1b07
//TODO: VPALIGNR $7, X2, X9, X11 // c463310fda07
//TODO: VPALIGNR $7, X11, X9, X11 // c443310fdb07
//TODO: VPALIGNR $7, (BX), Y15, Y2 // c4e3050f1307
//TODO: VPALIGNR $7, (R11), Y15, Y2 // c4c3050f1307
//TODO: VPALIGNR $7, Y2, Y15, Y2 // c4e3050fd207
//TODO: VPALIGNR $7, Y11, Y15, Y2 // c4c3050fd307
//TODO: VPALIGNR $7, (BX), Y15, Y11 // c463050f1b07
//TODO: VPALIGNR $7, (R11), Y15, Y11 // c443050f1b07
//TODO: VPALIGNR $7, Y2, Y15, Y11 // c463050fda07
//TODO: VPALIGNR $7, Y11, Y15, Y11 // c443050fdb07
VPALIGNR $7, (BX), Y15, Y2 // c4e3050f1307
VPALIGNR $7, (R11), Y15, Y2 // c4c3050f1307
VPALIGNR $7, Y2, Y15, Y2 // c4e3050fd207
VPALIGNR $7, Y11, Y15, Y2 // c4c3050fd307
VPALIGNR $7, (BX), Y15, Y11 // c463050f1b07
VPALIGNR $7, (R11), Y15, Y11 // c443050f1b07
VPALIGNR $7, Y2, Y15, Y11 // c463050fda07
VPALIGNR $7, Y11, Y15, Y11 // c443050fdb07
VPAND (BX), X9, X2 // c4e131db13 or c5b1db13
VPAND (R11), X9, X2 // c4c131db13
VPAND X2, X9, X2 // c4e131dbd2 or c5b1dbd2
@ -8342,14 +8342,14 @@ TEXT asmtest(SB),7,$0
//TODO: VPBLENDD $7, (R11), X9, X11 // c44331021b07
//TODO: VPBLENDD $7, X2, X9, X11 // c4633102da07
//TODO: VPBLENDD $7, X11, X9, X11 // c4433102db07
//TODO: VPBLENDD $7, (BX), Y15, Y2 // c4e305021307
//TODO: VPBLENDD $7, (R11), Y15, Y2 // c4c305021307
//TODO: VPBLENDD $7, Y2, Y15, Y2 // c4e30502d207
//TODO: VPBLENDD $7, Y11, Y15, Y2 // c4c30502d307
//TODO: VPBLENDD $7, (BX), Y15, Y11 // c46305021b07
//TODO: VPBLENDD $7, (R11), Y15, Y11 // c44305021b07
//TODO: VPBLENDD $7, Y2, Y15, Y11 // c4630502da07
//TODO: VPBLENDD $7, Y11, Y15, Y11 // c4430502db07
VPBLENDD $7, (BX), Y15, Y2 // c4e305021307
VPBLENDD $7, (R11), Y15, Y2 // c4c305021307
VPBLENDD $7, Y2, Y15, Y2 // c4e30502d207
VPBLENDD $7, Y11, Y15, Y2 // c4c30502d307
VPBLENDD $7, (BX), Y15, Y11 // c46305021b07
VPBLENDD $7, (R11), Y15, Y11 // c44305021b07
VPBLENDD $7, Y2, Y15, Y11 // c4630502da07
VPBLENDD $7, Y11, Y15, Y11 // c4430502db07
//TODO: VPBLENDVB XMM12, (BX), X9, X2 // c4e3314c13c0
//TODO: VPBLENDVB XMM12, (R11), X9, X2 // c4c3314c13c0
//TODO: VPBLENDVB XMM12, X2, X9, X2 // c4e3314cd2c0
@ -8614,22 +8614,22 @@ TEXT asmtest(SB),7,$0
//TODO: VPCMPISTRM $7, (R11), X11 // c44379621b07
//TODO: VPCMPISTRM $7, X2, X11 // c4637962da07
//TODO: VPCMPISTRM $7, X11, X11 // c4437962db07
//TODO: VPERM2F128 $7, (BX), Y15, Y2 // c4e305061307
//TODO: VPERM2F128 $7, (R11), Y15, Y2 // c4c305061307
//TODO: VPERM2F128 $7, Y2, Y15, Y2 // c4e30506d207
//TODO: VPERM2F128 $7, Y11, Y15, Y2 // c4c30506d307
//TODO: VPERM2F128 $7, (BX), Y15, Y11 // c46305061b07
//TODO: VPERM2F128 $7, (R11), Y15, Y11 // c44305061b07
//TODO: VPERM2F128 $7, Y2, Y15, Y11 // c4630506da07
//TODO: VPERM2F128 $7, Y11, Y15, Y11 // c4430506db07
//TODO: VPERM2I128 $7, (BX), Y15, Y2 // c4e305461307
//TODO: VPERM2I128 $7, (R11), Y15, Y2 // c4c305461307
//TODO: VPERM2I128 $7, Y2, Y15, Y2 // c4e30546d207
//TODO: VPERM2I128 $7, Y11, Y15, Y2 // c4c30546d307
//TODO: VPERM2I128 $7, (BX), Y15, Y11 // c46305461b07
//TODO: VPERM2I128 $7, (R11), Y15, Y11 // c44305461b07
//TODO: VPERM2I128 $7, Y2, Y15, Y11 // c4630546da07
//TODO: VPERM2I128 $7, Y11, Y15, Y11 // c4430546db07
VPERM2F128 $7, (BX), Y15, Y2 // c4e305061307
VPERM2F128 $7, (R11), Y15, Y2 // c4c305061307
VPERM2F128 $7, Y2, Y15, Y2 // c4e30506d207
VPERM2F128 $7, Y11, Y15, Y2 // c4c30506d307
VPERM2F128 $7, (BX), Y15, Y11 // c46305061b07
VPERM2F128 $7, (R11), Y15, Y11 // c44305061b07
VPERM2F128 $7, Y2, Y15, Y11 // c4630506da07
VPERM2F128 $7, Y11, Y15, Y11 // c4430506db07
VPERM2I128 $7, (BX), Y15, Y2 // c4e305461307
VPERM2I128 $7, (R11), Y15, Y2 // c4c305461307
VPERM2I128 $7, Y2, Y15, Y2 // c4e30546d207
VPERM2I128 $7, Y11, Y15, Y2 // c4c30546d307
VPERM2I128 $7, (BX), Y15, Y11 // c46305461b07
VPERM2I128 $7, (R11), Y15, Y11 // c44305461b07
VPERM2I128 $7, Y2, Y15, Y11 // c4630546da07
VPERM2I128 $7, Y11, Y15, Y11 // c4430546db07
//TODO: VPERMD (BX), Y15, Y2 // c4e2053613
//TODO: VPERMD (R11), Y15, Y2 // c4c2053613
//TODO: VPERMD Y2, Y15, Y2 // c4e20536d2
@ -9462,22 +9462,22 @@ TEXT asmtest(SB),7,$0
//TODO: VPMULUDQ (R11), Y15, Y11 // c44105f41b
//TODO: VPMULUDQ Y2, Y15, Y11 // c46105f4da or c505f4da
//TODO: VPMULUDQ Y11, Y15, Y11 // c44105f4db
//TODO: VPOR (BX), X9, X2 // c4e131eb13 or c5b1eb13
//TODO: VPOR (R11), X9, X2 // c4c131eb13
//TODO: VPOR X2, X9, X2 // c4e131ebd2 or c5b1ebd2
//TODO: VPOR X11, X9, X2 // c4c131ebd3
//TODO: VPOR (BX), X9, X11 // c46131eb1b or c531eb1b
//TODO: VPOR (R11), X9, X11 // c44131eb1b
//TODO: VPOR X2, X9, X11 // c46131ebda or c531ebda
//TODO: VPOR X11, X9, X11 // c44131ebdb
//TODO: VPOR (BX), Y15, Y2 // c4e105eb13 or c585eb13
//TODO: VPOR (R11), Y15, Y2 // c4c105eb13
//TODO: VPOR Y2, Y15, Y2 // c4e105ebd2 or c585ebd2
//TODO: VPOR Y11, Y15, Y2 // c4c105ebd3
//TODO: VPOR (BX), Y15, Y11 // c46105eb1b or c505eb1b
//TODO: VPOR (R11), Y15, Y11 // c44105eb1b
//TODO: VPOR Y2, Y15, Y11 // c46105ebda or c505ebda
//TODO: VPOR Y11, Y15, Y11 // c44105ebdb
VPOR (BX), X9, X2 // c4e131eb13 or c5b1eb13
VPOR (R11), X9, X2 // c4c131eb13
VPOR X2, X9, X2 // c4e131ebd2 or c5b1ebd2
VPOR X11, X9, X2 // c4c131ebd3
VPOR (BX), X9, X11 // c46131eb1b or c531eb1b
VPOR (R11), X9, X11 // c44131eb1b
VPOR X2, X9, X11 // c46131ebda or c531ebda
VPOR X11, X9, X11 // c44131ebdb
VPOR (BX), Y15, Y2 // c4e105eb13 or c585eb13
VPOR (R11), Y15, Y2 // c4c105eb13
VPOR Y2, Y15, Y2 // c4e105ebd2 or c585ebd2
VPOR Y11, Y15, Y2 // c4c105ebd3
VPOR (BX), Y15, Y11 // c46105eb1b or c505eb1b
VPOR (R11), Y15, Y11 // c44105eb1b
VPOR Y2, Y15, Y11 // c46105ebda or c505ebda
VPOR Y11, Y15, Y11 // c44105ebdb
//TODO: VPSADBW (BX), X9, X2 // c4e131f613 or c5b1f613
//TODO: VPSADBW (R11), X9, X2 // c4c131f613
//TODO: VPSADBW X2, X9, X2 // c4e131f6d2 or c5b1f6d2
@ -9494,38 +9494,38 @@ TEXT asmtest(SB),7,$0
//TODO: VPSADBW (R11), Y15, Y11 // c44105f61b
//TODO: VPSADBW Y2, Y15, Y11 // c46105f6da or c505f6da
//TODO: VPSADBW Y11, Y15, Y11 // c44105f6db
//TODO: VPSHUFB (BX), X9, X2 // c4e2310013
//TODO: VPSHUFB (R11), X9, X2 // c4c2310013
//TODO: VPSHUFB X2, X9, X2 // c4e23100d2
//TODO: VPSHUFB X11, X9, X2 // c4c23100d3
//TODO: VPSHUFB (BX), X9, X11 // c46231001b
//TODO: VPSHUFB (R11), X9, X11 // c44231001b
//TODO: VPSHUFB X2, X9, X11 // c4623100da
//TODO: VPSHUFB X11, X9, X11 // c4423100db
//TODO: VPSHUFB (BX), Y15, Y2 // c4e2050013
//TODO: VPSHUFB (R11), Y15, Y2 // c4c2050013
//TODO: VPSHUFB Y2, Y15, Y2 // c4e20500d2
//TODO: VPSHUFB Y11, Y15, Y2 // c4c20500d3
//TODO: VPSHUFB (BX), Y15, Y11 // c46205001b
//TODO: VPSHUFB (R11), Y15, Y11 // c44205001b
//TODO: VPSHUFB Y2, Y15, Y11 // c4620500da
//TODO: VPSHUFB Y11, Y15, Y11 // c4420500db
//TODO: VPSHUFD $7, (BX), X2 // c4e179701307 or c5f9701307
//TODO: VPSHUFD $7, (R11), X2 // c4c179701307
//TODO: VPSHUFD $7, X2, X2 // c4e17970d207 or c5f970d207
//TODO: VPSHUFD $7, X11, X2 // c4c17970d307
//TODO: VPSHUFD $7, (BX), X11 // c46179701b07 or c579701b07
//TODO: VPSHUFD $7, (R11), X11 // c44179701b07
//TODO: VPSHUFD $7, X2, X11 // c4617970da07 or c57970da07
//TODO: VPSHUFD $7, X11, X11 // c4417970db07
//TODO: VPSHUFD $7, (BX), Y2 // c4e17d701307 or c5fd701307
//TODO: VPSHUFD $7, (R11), Y2 // c4c17d701307
//TODO: VPSHUFD $7, Y2, Y2 // c4e17d70d207 or c5fd70d207
//TODO: VPSHUFD $7, Y11, Y2 // c4c17d70d307
//TODO: VPSHUFD $7, (BX), Y11 // c4617d701b07 or c57d701b07
//TODO: VPSHUFD $7, (R11), Y11 // c4417d701b07
//TODO: VPSHUFD $7, Y2, Y11 // c4617d70da07 or c57d70da07
//TODO: VPSHUFD $7, Y11, Y11 // c4417d70db07
VPSHUFB (BX), X9, X2 // c4e2310013
VPSHUFB (R11), X9, X2 // c4c2310013
VPSHUFB X2, X9, X2 // c4e23100d2
VPSHUFB X11, X9, X2 // c4c23100d3
VPSHUFB (BX), X9, X11 // c46231001b
VPSHUFB (R11), X9, X11 // c44231001b
VPSHUFB X2, X9, X11 // c4623100da
VPSHUFB X11, X9, X11 // c4423100db
VPSHUFB (BX), Y15, Y2 // c4e2050013
VPSHUFB (R11), Y15, Y2 // c4c2050013
VPSHUFB Y2, Y15, Y2 // c4e20500d2
VPSHUFB Y11, Y15, Y2 // c4c20500d3
VPSHUFB (BX), Y15, Y11 // c46205001b
VPSHUFB (R11), Y15, Y11 // c44205001b
VPSHUFB Y2, Y15, Y11 // c4620500da
VPSHUFB Y11, Y15, Y11 // c4420500db
VPSHUFD $7, (BX), X2 // c4e179701307 or c5f9701307
VPSHUFD $7, (R11), X2 // c4c179701307
VPSHUFD $7, X2, X2 // c4e17970d207 or c5f970d207
VPSHUFD $7, X11, X2 // c4c17970d307
VPSHUFD $7, (BX), X11 // c46179701b07 or c579701b07
VPSHUFD $7, (R11), X11 // c44179701b07
VPSHUFD $7, X2, X11 // c4617970da07 or c57970da07
VPSHUFD $7, X11, X11 // c4417970db07
VPSHUFD $7, (BX), Y2 // c4e17d701307 or c5fd701307
VPSHUFD $7, (R11), Y2 // c4c17d701307
VPSHUFD $7, Y2, Y2 // c4e17d70d207 or c5fd70d207
VPSHUFD $7, Y11, Y2 // c4c17d70d307
VPSHUFD $7, (BX), Y11 // c4617d701b07 or c57d701b07
VPSHUFD $7, (R11), Y11 // c4417d701b07
VPSHUFD $7, Y2, Y11 // c4617d70da07 or c57d70da07
VPSHUFD $7, Y11, Y11 // c4417d70db07
//TODO: VPSHUFHW $7, (BX), X2 // c4e17a701307 or c5fa701307
//TODO: VPSHUFHW $7, (R11), X2 // c4c17a701307
//TODO: VPSHUFHW $7, X2, X2 // c4e17a70d207 or c5fa70d207
@ -9606,30 +9606,30 @@ TEXT asmtest(SB),7,$0
//TODO: VPSIGNW (R11), Y15, Y11 // c44205091b
//TODO: VPSIGNW Y2, Y15, Y11 // c4620509da
//TODO: VPSIGNW Y11, Y15, Y11 // c4420509db
//TODO: VPSLLD (BX), X9, X2 // c4e131f213 or c5b1f213
//TODO: VPSLLD (R11), X9, X2 // c4c131f213
//TODO: VPSLLD X2, X9, X2 // c4e131f2d2 or c5b1f2d2
//TODO: VPSLLD X11, X9, X2 // c4c131f2d3
//TODO: VPSLLD (BX), X9, X11 // c46131f21b or c531f21b
//TODO: VPSLLD (R11), X9, X11 // c44131f21b
//TODO: VPSLLD X2, X9, X11 // c46131f2da or c531f2da
//TODO: VPSLLD X11, X9, X11 // c44131f2db
//TODO: VPSLLD $7, X2, X9 // c4e13172f207 or c5b172f207
//TODO: VPSLLD $7, X11, X9 // c4c13172f307
//TODO: VPSLLDQ $7, X2, X9 // c4e13173fa07 or c5b173fa07
//TODO: VPSLLDQ $7, X11, X9 // c4c13173fb07
//TODO: VPSLLDQ $7, Y2, Y15 // c4e10573fa07 or c58573fa07
//TODO: VPSLLDQ $7, Y11, Y15 // c4c10573fb07
//TODO: VPSLLQ (BX), X9, X2 // c4e131f313 or c5b1f313
//TODO: VPSLLQ (R11), X9, X2 // c4c131f313
//TODO: VPSLLQ X2, X9, X2 // c4e131f3d2 or c5b1f3d2
//TODO: VPSLLQ X11, X9, X2 // c4c131f3d3
//TODO: VPSLLQ (BX), X9, X11 // c46131f31b or c531f31b
//TODO: VPSLLQ (R11), X9, X11 // c44131f31b
//TODO: VPSLLQ X2, X9, X11 // c46131f3da or c531f3da
//TODO: VPSLLQ X11, X9, X11 // c44131f3db
//TODO: VPSLLQ $7, X2, X9 // c4e13173f207 or c5b173f207
//TODO: VPSLLQ $7, X11, X9 // c4c13173f307
VPSLLD (BX), X9, X2 // c4e131f213 or c5b1f213
VPSLLD (R11), X9, X2 // c4c131f213
VPSLLD X2, X9, X2 // c4e131f2d2 or c5b1f2d2
VPSLLD X11, X9, X2 // c4c131f2d3
VPSLLD (BX), X9, X11 // c46131f21b or c531f21b
VPSLLD (R11), X9, X11 // c44131f21b
VPSLLD X2, X9, X11 // c46131f2da or c531f2da
VPSLLD X11, X9, X11 // c44131f2db
VPSLLD $7, X2, X9 // c4e13172f207 or c5b172f207
VPSLLD $7, X11, X9 // c4c13172f307
VPSLLDQ $7, X2, X9 // c4e13173fa07 or c5b173fa07
VPSLLDQ $7, X11, X9 // c4c13173fb07
VPSLLDQ $7, Y2, Y15 // c4e10573fa07 or c58573fa07
VPSLLDQ $7, Y11, Y15 // c4c10573fb07
VPSLLQ (BX), X9, X2 // c4e131f313 or c5b1f313
VPSLLQ (R11), X9, X2 // c4c131f313
VPSLLQ X2, X9, X2 // c4e131f3d2 or c5b1f3d2
VPSLLQ X11, X9, X2 // c4c131f3d3
VPSLLQ (BX), X9, X11 // c46131f31b or c531f31b
VPSLLQ (R11), X9, X11 // c44131f31b
VPSLLQ X2, X9, X11 // c46131f3da or c531f3da
VPSLLQ X11, X9, X11 // c44131f3db
VPSLLQ $7, X2, X9 // c4e13173f207 or c5b173f207
VPSLLQ $7, X11, X9 // c4c13173f307
//TODO: VPSLLVD (BX), X9, X2 // c4e2314713
//TODO: VPSLLVD (R11), X9, X2 // c4c2314713
//TODO: VPSLLVD X2, X9, X2 // c4e23147d2
@ -9738,30 +9738,30 @@ TEXT asmtest(SB),7,$0
//TODO: VPSRAW X11, Y15, Y11 // c44105e1db
//TODO: VPSRAW $7, Y2, Y15 // c4e10571e207 or c58571e207
//TODO: VPSRAW $7, Y11, Y15 // c4c10571e307
//TODO: VPSRLD (BX), X9, X2 // c4e131d213 or c5b1d213
//TODO: VPSRLD (R11), X9, X2 // c4c131d213
//TODO: VPSRLD X2, X9, X2 // c4e131d2d2 or c5b1d2d2
//TODO: VPSRLD X11, X9, X2 // c4c131d2d3
//TODO: VPSRLD (BX), X9, X11 // c46131d21b or c531d21b
//TODO: VPSRLD (R11), X9, X11 // c44131d21b
//TODO: VPSRLD X2, X9, X11 // c46131d2da or c531d2da
//TODO: VPSRLD X11, X9, X11 // c44131d2db
//TODO: VPSRLD $7, X2, X9 // c4e13172d207 or c5b172d207
//TODO: VPSRLD $7, X11, X9 // c4c13172d307
//TODO: VPSRLDQ $7, X2, X9 // c4e13173da07 or c5b173da07
//TODO: VPSRLDQ $7, X11, X9 // c4c13173db07
//TODO: VPSRLDQ $7, Y2, Y15 // c4e10573da07 or c58573da07
//TODO: VPSRLDQ $7, Y11, Y15 // c4c10573db07
//TODO: VPSRLQ (BX), X9, X2 // c4e131d313 or c5b1d313
//TODO: VPSRLQ (R11), X9, X2 // c4c131d313
//TODO: VPSRLQ X2, X9, X2 // c4e131d3d2 or c5b1d3d2
//TODO: VPSRLQ X11, X9, X2 // c4c131d3d3
//TODO: VPSRLQ (BX), X9, X11 // c46131d31b or c531d31b
//TODO: VPSRLQ (R11), X9, X11 // c44131d31b
//TODO: VPSRLQ X2, X9, X11 // c46131d3da or c531d3da
//TODO: VPSRLQ X11, X9, X11 // c44131d3db
//TODO: VPSRLQ $7, X2, X9 // c4e13173d207 or c5b173d207
//TODO: VPSRLQ $7, X11, X9 // c4c13173d307
VPSRLD (BX), X9, X2 // c4e131d213 or c5b1d213
VPSRLD (R11), X9, X2 // c4c131d213
VPSRLD X2, X9, X2 // c4e131d2d2 or c5b1d2d2
VPSRLD X11, X9, X2 // c4c131d2d3
VPSRLD (BX), X9, X11 // c46131d21b or c531d21b
VPSRLD (R11), X9, X11 // c44131d21b
VPSRLD X2, X9, X11 // c46131d2da or c531d2da
VPSRLD X11, X9, X11 // c44131d2db
VPSRLD $7, X2, X9 // c4e13172d207 or c5b172d207
VPSRLD $7, X11, X9 // c4c13172d307
VPSRLDQ $7, X2, X9 // c4e13173da07 or c5b173da07
VPSRLDQ $7, X11, X9 // c4c13173db07
VPSRLDQ $7, Y2, Y15 // c4e10573da07 or c58573da07
VPSRLDQ $7, Y11, Y15 // c4c10573db07
VPSRLQ (BX), X9, X2 // c4e131d313 or c5b1d313
VPSRLQ (R11), X9, X2 // c4c131d313
VPSRLQ X2, X9, X2 // c4e131d3d2 or c5b1d3d2
VPSRLQ X11, X9, X2 // c4c131d3d3
VPSRLQ (BX), X9, X11 // c46131d31b or c531d31b
VPSRLQ (R11), X9, X11 // c44131d31b
VPSRLQ X2, X9, X11 // c46131d3da or c531d3da
VPSRLQ X11, X9, X11 // c44131d3db
VPSRLQ $7, X2, X9 // c4e13173d207 or c5b173d207
VPSRLQ $7, X11, X9 // c4c13173d307
//TODO: VPSRLVD (BX), X9, X2 // c4e2314513
//TODO: VPSRLVD (R11), X9, X2 // c4c2314513
//TODO: VPSRLVD X2, X9, X2 // c4e23145d2

View file

@ -1089,6 +1089,8 @@ func (p *Package) gccMachine() []string {
return []string{"-m31"}
case "s390x":
return []string{"-m64"}
case "mips64", "mips64le":
return []string{"-mabi=64"}
}
return nil
}
@ -1241,12 +1243,20 @@ func (p *Package) gccErrors(stdin []byte) string {
// TODO(rsc): require failure
args := p.gccCmd()
// Optimization options can confuse the error messages; remove them.
nargs := make([]string, 0, len(args))
for _, arg := range args {
if !strings.HasPrefix(arg, "-O") {
nargs = append(nargs, arg)
}
}
if *debugGcc {
fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(nargs, " "))
os.Stderr.Write(stdin)
fmt.Fprint(os.Stderr, "EOF\n")
}
stdout, stderr, _ := run(stdin, args)
stdout, stderr, _ := run(stdin, nargs)
if *debugGcc {
os.Stderr.Write(stdout)
os.Stderr.Write(stderr)

View file

@ -175,10 +175,11 @@ func (p *Package) writeDefs() {
}
fmt.Fprintf(fgo2, "\n")
callsMalloc := false
for _, key := range nameKeys(p.Name) {
n := p.Name[key]
if n.FuncType != nil {
p.writeDefsFunc(fgo2, n)
p.writeDefsFunc(fgo2, n, &callsMalloc)
}
}
@ -189,6 +190,12 @@ func (p *Package) writeDefs() {
} else {
p.writeExports(fgo2, fm, fgcc, fgcch)
}
if callsMalloc && !*gccgo {
fmt.Fprint(fgo2, strings.Replace(cMallocDefGo, "PREFIX", cPrefix, -1))
fmt.Fprint(fgcc, strings.Replace(strings.Replace(cMallocDefC, "PREFIX", cPrefix, -1), "PACKED", p.packedAttribute(), -1))
}
if err := fgcc.Close(); err != nil {
fatalf("%s", err)
}
@ -352,7 +359,7 @@ func (p *Package) structType(n *Name) (string, int64) {
return buf.String(), off
}
func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name) {
func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name, callsMalloc *bool) {
name := n.Go
gtype := n.FuncType.Go
void := gtype.Results == nil || len(gtype.Results.List) == 0
@ -441,6 +448,9 @@ func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name) {
if inProlog {
fmt.Fprint(fgo2, builtinDefs[name])
if strings.Contains(builtinDefs[name], "_cgo_cmalloc") {
*callsMalloc = true
}
return
}
@ -560,6 +570,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Gcc wrapper unpacks the C argument struct
// and calls the actual C function.
fmt.Fprintf(fgcc, "CGO_NO_SANITIZE_THREAD\n")
if n.AddError {
fmt.Fprintf(fgcc, "int\n")
} else {
@ -635,6 +646,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// wrapper, we can't refer to the function, since the reference is in
// a different file.
func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
fmt.Fprintf(fgcc, "CGO_NO_SANITIZE_THREAD\n")
if t := n.FuncType.Result; t != nil {
fmt.Fprintf(fgcc, "%s\n", t.C.String())
} else {
@ -710,11 +722,13 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
p.writeExportHeader(fgcch)
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
fmt.Fprintf(fgcc, "#include <stdlib.h>\n")
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n")
fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *, int, __SIZE_TYPE__), void *, int, __SIZE_TYPE__);\n")
fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n")
fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);")
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
for _, exp := range p.ExpFunc {
@ -817,6 +831,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcch, "\nextern %s;\n", s)
fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD")
fmt.Fprintf(fgcc, "\n%s\n", s)
fmt.Fprintf(fgcc, "{\n")
fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n")
@ -1020,7 +1035,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcc, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName)
fmt.Fprint(fgcc, "\n")
fmt.Fprint(fgcc, "\n")
fmt.Fprint(fgcc, "\nCGO_NO_SANITIZE_THREAD\n")
fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams)
if resultCount > 0 {
fmt.Fprintf(fgcc, "\t%s r;\n", cRet)
@ -1304,11 +1319,14 @@ extern char* _cgo_topofstack(void);
// Prologue defining TSAN functions in C.
const noTsanProlog = `
#define CGO_NO_SANITIZE_THREAD
#define _cgo_tsan_acquire()
#define _cgo_tsan_release()
`
const yesTsanProlog = `
#define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread))
long long _cgo_sync __attribute__ ((common));
extern void __tsan_acquire(void*);
@ -1346,9 +1364,6 @@ const goProlog = `
//go:linkname _cgo_runtime_cgocall runtime.cgocall
func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32
//go:linkname _cgo_runtime_cmalloc runtime.cmalloc
func _cgo_runtime_cmalloc(uintptr) unsafe.Pointer
//go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr)
@ -1360,10 +1375,8 @@ func _cgoCheckResult(interface{})
`
const gccgoGoProlog = `
//extern runtime.cgoCheckPointer
func _cgoCheckPointer(interface{}, ...interface{}) interface{}
//extern runtime.cgoCheckResult
func _cgoCheckResult(interface{})
`
@ -1396,7 +1409,7 @@ func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
const cStringDef = `
func _Cfunc_CString(s string) *_Ctype_char {
p := _cgo_runtime_cmalloc(uintptr(len(s)+1))
p := _cgo_cmalloc(uint64(len(s)+1))
pp := (*[1<<30]byte)(p)
copy(pp[:], s)
pp[len(s)] = 0
@ -1406,7 +1419,7 @@ func _Cfunc_CString(s string) *_Ctype_char {
const cBytesDef = `
func _Cfunc_CBytes(b []byte) unsafe.Pointer {
p := _cgo_runtime_cmalloc(uintptr(len(b)))
p := _cgo_cmalloc(uint64(len(b)))
pp := (*[1<<30]byte)(p)
copy(pp[:], b)
return p
@ -1415,7 +1428,7 @@ func _Cfunc_CBytes(b []byte) unsafe.Pointer {
const cMallocDef = `
func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
return _cgo_runtime_cmalloc(uintptr(n))
return _cgo_cmalloc(uint64(n))
}
`
@ -1428,6 +1441,50 @@ var builtinDefs = map[string]string{
"_CMalloc": cMallocDef,
}
// Definitions for C.malloc in Go and in C. We define it ourselves
// since we call it from functions we define, such as C.CString.
// Also, we have historically ensured that C.malloc does not return
// nil even for an allocation of 0.
const cMallocDefGo = `
//go:cgo_import_static _cgoPREFIX_Cfunc__Cmalloc
//go:linkname __cgofn__cgoPREFIX_Cfunc__Cmalloc _cgoPREFIX_Cfunc__Cmalloc
var __cgofn__cgoPREFIX_Cfunc__Cmalloc byte
var _cgoPREFIX_Cfunc__Cmalloc = unsafe.Pointer(&__cgofn__cgoPREFIX_Cfunc__Cmalloc)
//go:cgo_unsafe_args
func _cgo_cmalloc(p0 uint64) (r1 unsafe.Pointer) {
_cgo_runtime_cgocall(_cgoPREFIX_Cfunc__Cmalloc, uintptr(unsafe.Pointer(&p0)))
return
}
`
// cMallocDefC defines the C version of C.malloc for the gc compiler.
// It is defined here because C.CString and friends need a definition.
// We define it by hand, rather than simply inventing a reference to
// C.malloc, because <stdlib.h> may not have been included.
// This is approximately what writeOutputFunc would generate, but
// skips the cgo_topofstack code (which is only needed if the C code
// calls back into Go). This also avoids returning nil for an
// allocation of 0 bytes.
const cMallocDefC = `
CGO_NO_SANITIZE_THREAD
void _cgoPREFIX_Cfunc__Cmalloc(void *v) {
struct {
unsigned long long p0;
void *r1;
} PACKED *a = v;
void *ret;
_cgo_tsan_acquire();
ret = malloc(a->p0);
if (ret == 0 && a->p0 == 0) {
ret = malloc(1);
}
a->r1 = ret;
_cgo_tsan_release();
}
`
func (p *Package) cPrologGccgo() string {
return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1),
"GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1)

View file

@ -60,8 +60,15 @@ Flags:
-installsuffix suffix
Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix
instead of $GOROOT/pkg/$GOOS_$GOARCH.
-l
Disable inlining.
-largemodel
Generated code that assumes a large memory model.
Generate code that assumes a large memory model.
-linkobj file
Write linker-specific object to file and compiler-specific
object to usual output file (as specified by -o).
Without this flag, the -o output is a combination of both
linker and compiler input.
-memprofile file
Write memory profile for the compilation to file.
-memprofilerate rate

View file

@ -25,18 +25,16 @@ func betypeinit() {
cmpptr = x86.ACMPL
}
if gc.Ctxt.Flag_dynlink {
gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15)
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
}
func Main() {
if obj.Getgoos() == "nacl" {
resvd = append(resvd, x86.REG_BP, x86.REG_R15)
} else if obj.Framepointer_enabled != 0 {
resvd = append(resvd, x86.REG_BP)
}
gc.Thearch.LinkArch = &x86.Linkamd64
if obj.Getgoarch() == "amd64p32" {
gc.Thearch.LinkArch = &x86.Linkamd64p32
@ -51,7 +49,6 @@ func Main() {
gc.Thearch.FREGMIN = x86.REG_X0
gc.Thearch.FREGMAX = x86.REG_X15
gc.Thearch.MAXWIDTH = 1 << 50
gc.Thearch.ReservedRegs = resvd
gc.Thearch.AddIndex = addindex
gc.Thearch.Betypeinit = betypeinit

View file

@ -116,7 +116,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
base = n1.Left
}
if base.Op == gc.ONAME && base.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG {
if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG {
r1 = *n1
} else {
gc.Regalloc(&r1, t, n1)
@ -229,6 +229,8 @@ func gmove(f *gc.Node, t *gc.Node) {
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))
/*

View file

@ -32,7 +32,6 @@ package amd64
import (
"cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
@ -121,7 +120,7 @@ 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 obj.Framepointer_enabled != 0 {
} else if gc.Ctxt.Framepointer_enabled {
// BP is part of the calling convention if framepointer_enabled.
b &^= (1 << (x86.REG_BP - x86.REG_AX))
}

View file

@ -878,6 +878,18 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
gc.Gvarkill(v.Aux.(*gc.Node))
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)
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.

View file

@ -86,17 +86,8 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
n = &n1
case gc.ONAME:
if n.Class == gc.PPARAMREF {
var n1 gc.Node
gc.Cgen(n.Name.Heapaddr, &n1)
sclean[nsclean-1] = n1
n = &n1
}
case gc.ONAME, gc.OINDREG:
// nothing
case gc.OINDREG:
break
}
*lo = *n

View file

@ -55,12 +55,15 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
}
f.Offset = o
if f.Nname != nil {
// this same stackparam logic is in addrescapes
// in typecheck.go. usually addrescapes runs after
// widstruct, in which case we could drop this,
// addrescapes has similar code to update these offsets.
// Usually addrescapes runs after widstruct,
// in which case we could drop this,
// but function closure functions are the exception.
if f.Nname.Name.Param.Stackparam != nil {
f.Nname.Name.Param.Stackparam.Xoffset = o
// NOTE(rsc): This comment may be stale.
// It's possible the ordering has changed and this is
// now the common case. I'm not sure.
if f.Nname.Name.Param.Stackcopy != nil {
f.Nname.Name.Param.Stackcopy.Xoffset = o
f.Nname.Xoffset = 0
} else {
f.Nname.Xoffset = o

View file

@ -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 gc
import (
"bytes"
"fmt"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
)
// 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) {
testenv.MustHaveGoBuild(t)
if runtime.GOOS == "windows" {
// TODO: remove if we can get "go tool compile -S" to work on windows.
t.Skipf("skipping test: recursive windows compile not working")
}
dir, err := ioutil.TempDir("", "TestAssembly")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(dir)
for _, test := range asmTests {
asm := compileToAsm(dir, test.arch, 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]
}
for _, r := range test.regexps {
if b, err := regexp.MatchString(r, asm); !b || err != nil {
t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, test.function, asm)
}
}
}
}
// 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 {
// Create source.
src := filepath.Join(dir, "test.go")
f, err := os.Create(src)
if err != nil {
panic(err)
}
f.Write([]byte(pkg))
f.Close()
var stdout, stderr bytes.Buffer
cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
cmd.Env = append(cmd.Env, "GOARCH="+arch)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
panic(err)
}
if s := stderr.String(); s != "" {
panic(fmt.Errorf("Stderr = %s\nWant empty", s))
}
return stdout.String()
}
// template to convert a function to a full file
const template = `
package main
%s
`
type asmTest struct {
// architecture to compile to
arch string
// function to compile
function string
// regexps that must match the generated assembly
regexps []string
}
var asmTests = [...]asmTest{
{"amd64", `
func f(x int) int {
return x * 64
}
`,
[]string{"\tSHLQ\t\\$6,"},
},
{"amd64", `
func f(x int) int {
return x * 96
}`,
[]string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
},
}

View file

@ -6,7 +6,7 @@
// (see fmt.go, parser.go as "documentation" for how to use/setup data structures)
/*
Export data encoding:
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,
@ -49,7 +49,7 @@ Before exporting or importing, the type tables are populated with the
predeclared types (int, string, error, unsafe.Pointer, etc.). This way
they are automatically encoded with a known and fixed type index.
Encoding format:
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
@ -84,6 +84,43 @@ each encoding routine there is a matching and symmetric decoding routine.
This symmetry makes it very easy to change or extend the format: If a new
field needs to be encoded, a symmetric change can be made to exporter and
importer.
3) Making changes to the encoding format:
Any change to the encoding format requires a respective change in the
exporter below and a corresponding symmetric change to the importer in
bimport.go.
Furthermore, it requires a corresponding change to go/internal/gcimporter
and golang.org/x/tools/go/gcimporter15. Changes to the latter must preserve
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.
*/
package gc
@ -125,6 +162,17 @@ const exportVersion = "v0"
// Leave for debugging.
const exportInlined = true // default: true
// trackAllTypes enables cycle tracking for all types, not just named
// types. The existing compiler invariants assume that unnamed types
// that are not completely set up are not used, or else there are spurious
// 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).
// TODO(gri) enable and remove once issues caused by it are fixed
const trackAllTypes = false
type exporter struct {
out *bufio.Writer
@ -159,6 +207,10 @@ func export(out *bufio.Writer, trace bool) int {
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
if debugFormat {
@ -166,6 +218,12 @@ func export(out *bufio.Writer, trace bool) int {
}
p.rawByte(format)
format = 'n' // track named types only
if trackAllTypes {
format = 'a'
}
p.rawByte(format)
// posInfo exported or not?
p.bool(p.posInfoFormat)
@ -585,14 +643,21 @@ func (p *exporter) typ(t *Type) {
}
// otherwise, remember the type, write the type tag (< 0) and type data
if trackAllTypes {
if p.trace {
p.tracef("T%d = {>\n", len(p.typIndex))
defer p.tracef("<\n} ")
}
p.typIndex[t] = len(p.typIndex)
}
// pick off named types
if tsym := t.Sym; tsym != nil {
if !trackAllTypes {
// if we don't track all types, track named types now
p.typIndex[t] = len(p.typIndex)
}
// Predeclared types should have been found in the type map.
if t.Orig == t {
Fatalf("exporter: predeclared type missing from type map?")
@ -909,7 +974,7 @@ func parName(f *Field, numbered bool) string {
// print symbol with Vargen number or not as desired
name := s.Name
if strings.Contains(name, ".") {
panic("invalid symbol name: " + name)
Fatalf("invalid symbol name: %s", name)
}
// Functions that can be inlined use numbered parameters so we can distingish them
@ -1012,6 +1077,8 @@ func (p *exporter) float(x *Mpflt) {
// but instead of emitting the information textually, emit the node tree in
// binary form.
// TODO(gri) Improve tracing output. The current format is difficult to read.
// stmtList may emit more (or fewer) than len(list) nodes.
func (p *exporter) stmtList(list Nodes) {
if p.trace {
@ -1088,6 +1155,16 @@ func (p *exporter) expr(n *Node) {
defer p.tracef(") ")
}
// from nodefmt (fmt.go)
//
// nodefmt reverts nodes back to their original - we don't need to do
// it because we are not bound to produce valid Go syntax when exporting
//
// if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil {
// n = n.Orig
// }
// from exprfmt (fmt.go)
for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) {
n = n.Left
}
@ -1117,15 +1194,13 @@ func (p *exporter) expr(n *Node) {
// Special case: name used as local variable in export.
// _ becomes ~b%d internally; print as _ for export
if n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
// case 0: mapped to ONAME
p.op(ONAME)
p.bool(true) // indicate blank identifier
p.string("_") // inlined and customized version of p.sym(n)
break
}
if n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 {
// case 1: mapped to OPACK
p.op(OPACK)
p.op(ONAME)
p.sym(n)
break
}
@ -1134,24 +1209,18 @@ func (p *exporter) expr(n *Node) {
// 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 n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME {
// case 2: mapped to ONAME
p.op(ONAME)
// TODO(gri) can we map this case directly to OXDOT
// and then get rid of the bool here?
p.bool(false) // indicate non-blank identifier
p.typ(n.Left.Type)
p.op(OXDOT)
p.expr(n.Left) // n.Left.Op == OTYPE
p.fieldSym(n.Right.Sym, true)
break
}
// case 3: mapped to OPACK
p.op(OPACK)
p.sym(n) // fallthrough inlined here
case OPACK, ONONAME:
p.op(op)
p.op(ONAME)
p.sym(n)
// case OPACK, ONONAME:
// should have been resolved by typechecking - handled by default case
case OTYPE:
p.op(OTYPE)
if p.bool(n.Type == nil) {
@ -1160,14 +1229,14 @@ func (p *exporter) expr(n *Node) {
p.typ(n.Type)
}
case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
panic("unreachable") // should have been resolved by typechecking
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// should have been resolved by typechecking - handled by default case
// case OCLOSURE:
// unimplemented - handled by default case
// case OCOMPLIT:
// unimplemented - handled by default case
// should have been resolved by typechecking - handled by default case
case OPTRLIT:
p.op(OPTRLIT)
@ -1176,16 +1245,12 @@ func (p *exporter) expr(n *Node) {
case OSTRUCTLIT:
p.op(OSTRUCTLIT)
if !p.bool(n.Implicit) {
p.typ(n.Type)
}
p.elemList(n.List) // special handling of field names
case OARRAYLIT, OMAPLIT:
p.op(op)
if !p.bool(n.Implicit) {
p.op(OCOMPLIT)
p.typ(n.Type)
}
p.exprList(n.List)
case OKEY:
@ -1198,9 +1263,6 @@ func (p *exporter) expr(n *Node) {
case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
p.op(OXDOT)
p.expr(n.Left)
if n.Sym == nil {
panic("unreachable") // can this happen during export?
}
p.fieldSym(n.Sym, true)
case ODOTTYPE, ODOTTYPE2:
@ -1231,26 +1293,35 @@ func (p *exporter) expr(n *Node) {
p.expr(max)
case OCOPY, OCOMPLEX:
// treated like other builtin calls (see e.g., OREAL)
p.op(op)
p.expr(n.Left)
p.expr(n.Right)
p.op(OEND)
case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR:
p.op(OCONV)
p.typ(n.Type)
if p.bool(n.Left != nil) {
if n.Left != nil {
p.expr(n.Left)
p.op(OEND)
} else {
p.exprList(n.List)
p.exprList(n.List) // emits terminating OEND
}
case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
p.op(op)
if p.bool(n.Left != nil) {
if n.Left != nil {
p.expr(n.Left)
p.op(OEND)
} else {
p.exprList(n.List)
p.exprList(n.List) // emits terminating OEND
}
// only append() calls may contain '...' arguments
if op == OAPPEND {
p.bool(n.Isddd)
} else if n.Isddd {
Fatalf("exporter: unexpected '...' with %s call", opnames[op])
}
case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
@ -1306,7 +1377,8 @@ func (p *exporter) expr(n *Node) {
p.op(ODCLCONST)
default:
Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", n.Op)
Fatalf("cannot export %s (%d) node\n"+
"==> please file an issue and assign to gri@\n", n.Op, n.Op)
}
}
@ -1336,8 +1408,8 @@ func (p *exporter) stmt(n *Node) {
switch op := n.Op; op {
case ODCL:
p.op(ODCL)
switch n.Left.Class &^ PHEAP {
case PPARAM, PPARAMOUT, PAUTO:
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.
@ -1370,10 +1442,7 @@ func (p *exporter) stmt(n *Node) {
p.expr(n.Right)
}
case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
fallthrough
case OAS2:
case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
p.op(OAS2)
p.exprList(n.List)
p.exprList(n.Rlist)
@ -1382,9 +1451,8 @@ func (p *exporter) stmt(n *Node) {
p.op(ORETURN)
p.exprList(n.List)
case ORETJMP:
// generated by compiler for trampolin routines - not exported
panic("unreachable")
// case ORETJMP:
// unreachable - generated by compiler for trampolin routines
case OPROC, ODEFER:
p.op(op)
@ -1420,19 +1488,18 @@ func (p *exporter) stmt(n *Node) {
p.stmtList(n.List)
p.stmtList(n.Nbody)
case OFALL:
op = OXFALL
fallthrough
case OFALL, OXFALL:
p.op(OXFALL)
case OBREAK, OCONTINUE, OGOTO, OXFALL:
case OBREAK, OCONTINUE:
p.op(op)
p.exprsOrNil(n.Left, nil)
case OEMPTY:
// nothing to emit
case OLABEL:
p.op(OLABEL)
case OGOTO, OLABEL:
p.op(op)
p.expr(n.Left)
default:
@ -1475,6 +1542,8 @@ func (p *exporter) fieldSym(s *Sym, short bool) {
}
}
// sym must encode the _ (blank) identifier as a single string "_" since
// encoding for some nodes is based on this assumption (e.g. ONAME nodes).
func (p *exporter) sym(n *Node) {
s := n.Sym
if s.Pkg != nil {

View file

@ -3,7 +3,8 @@
// license that can be found in the LICENSE file.
// Binary package import.
// Based loosely on x/tools/go/importer.
// See bexport.go for the export data format and how
// to make a format change.
package gc
@ -28,6 +29,7 @@ type importer struct {
pkgList []*Pkg
typList []*Type
funcList []*Node // nil entry means already declared
trackAllTypes bool
// for delayed type verification
cmpList []struct{ pt, t *Type }
@ -59,6 +61,8 @@ func Import(in *bufio.Reader) {
Fatalf("importer: invalid encoding format in export data: got %q; want 'c' or 'd'", format)
}
p.trackAllTypes = p.rawByte() == 'a'
p.posInfoFormat = p.bool()
// --- generic export data ---
@ -100,7 +104,9 @@ func Import(in *bufio.Reader) {
// --- compiler-specific export data ---
// read compiler-specific flags
importpkg.Safe = p.bool()
// read but ignore safemode bit (see issue #15772)
p.bool() // formerly: importpkg.Safe = p.bool()
// phase 2
objcount = 0
@ -230,7 +236,7 @@ func (p *importer) pkg() *Pkg {
// 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)))
Fatalf("importer: package path %q for pkg index %d", path, len(p.pkgList))
}
pkg := importpkg
@ -331,7 +337,9 @@ func (p *importer) pos() {
func (p *importer) newtyp(etype EType) *Type {
t := typ(etype)
if p.trackAllTypes {
p.typList = append(p.typList, t)
}
return t
}
@ -389,7 +397,13 @@ func (p *importer) typ() *Type {
// read underlying type
// parser.go:hidden_type
t0 := p.typ()
p.importtype(t, t0) // parser.go:hidden_import
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)
}
// interfaces don't have associated methods
if t0.IsInterface() {
@ -788,16 +802,11 @@ func (p *importer) node() *Node {
return n
case ONAME:
if p.bool() {
// "_"
// TODO(gri) avoid repeated "_" lookup
return mkname(Pkglookup("_", localpkg))
}
return NodSym(OXDOT, typenod(p.typ()), p.fieldSym())
case OPACK, ONONAME:
return mkname(p.sym())
// case OPACK, ONONAME:
// unreachable - should have been resolved by typechecking
case OTYPE:
if p.bool() {
return mkname(p.sym())
@ -810,12 +819,9 @@ func (p *importer) node() *Node {
// case OCLOSURE:
// unimplemented
// case OCOMPLIT:
// unimplemented
case OPTRLIT:
n := p.expr()
if !p.bool() /* !implicit, i.e. '&' operator*/ {
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)
@ -827,18 +833,15 @@ func (p *importer) node() *Node {
return n
case OSTRUCTLIT:
n := Nod(OCOMPLIT, nil, nil)
if !p.bool() {
n.Right = typenod(p.typ())
}
n.List.Set(p.elemList())
n := Nod(OCOMPLIT, nil, typenod(p.typ()))
n.List.Set(p.elemList()) // special handling of field names
return n
case OARRAYLIT, OMAPLIT:
n := Nod(OCOMPLIT, nil, nil)
if !p.bool() {
n.Right = typenod(p.typ())
}
// case OARRAYLIT, OMAPLIT:
// unreachable - mapped to case OCOMPLIT below by exporter
case OCOMPLIT:
n := Nod(OCOMPLIT, nil, typenod(p.typ()))
n.List.Set(p.exprList())
return n
@ -854,14 +857,7 @@ func (p *importer) node() *Node {
case OXDOT:
// see parser.new_dotname
obj := p.expr()
sel := p.fieldSym()
if obj.Op == OPACK {
s := restrictlookup(sel.Name, obj.Name.Pkg)
obj.Used = true
return oldname(s)
}
return NodSym(OXDOT, obj, sel)
return NodSym(OXDOT, p.expr(), p.fieldSym())
// case ODOTTYPE, ODOTTYPE2:
// unreachable - mapped to case ODOTTYPE below by exporter
@ -891,29 +887,18 @@ func (p *importer) node() *Node {
n.SetSliceBounds(low, high, max)
return n
case OCOPY, OCOMPLEX:
n := builtinCall(op)
n.List.Set([]*Node{p.expr(), p.expr()})
return n
// case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR:
// unreachable - mapped to OCONV case below by exporter
case OCONV:
n := Nod(OCALL, typenod(p.typ()), nil)
if p.bool() {
n.List.Set1(p.expr())
} else {
n.List.Set(p.exprList())
}
return n
case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
n := builtinCall(op)
if p.bool() {
n.List.Set1(p.expr())
} else {
n.List.Set(p.exprList())
if op == OAPPEND {
n.Isddd = p.bool()
}
return n
@ -1053,6 +1038,7 @@ func (p *importer) node() *Node {
case OXCASE:
markdcl()
n := Nod(OXCASE, nil, nil)
n.Xoffset = int64(block)
n.List.Set(p.exprList())
// TODO(gri) eventually we must declare variables for type switch
// statements (type switch statements are not yet exported)
@ -1063,23 +1049,32 @@ func (p *importer) node() *Node {
// case OFALL:
// unreachable - mapped to OXFALL case below by exporter
case OBREAK, OCONTINUE, OGOTO, OXFALL:
case OXFALL:
n := Nod(OXFALL, nil, nil)
n.Xoffset = int64(block)
return n
case OBREAK, OCONTINUE:
left, _ := p.exprsOrNil()
if left != nil {
left = newname(left.Sym)
}
return Nod(op, left, nil)
// case OEMPTY:
// unreachable - not emitted by exporter
case OLABEL:
n := Nod(OLABEL, p.expr(), nil)
n.Left.Sym = dclstack // context, for goto restrictions
case OGOTO, OLABEL:
n := Nod(op, newname(p.expr().Sym), nil)
n.Sym = dclstack // context, for goto restrictions
return n
case OEND:
return nil
default:
Fatalf("importer: %s (%d) node not yet supported", op, op)
Fatalf("cannot import %s (%d) node\n"+
"==> please file an issue and assign to gri@\n", op, op)
panic("unreachable") // satisfy compiler
}
}

View file

@ -3,106 +3,108 @@
package gc
const runtimeimport = "" +
"c\x00\x03v0\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\x15panic" +
"divide\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\x11gor" +
"ecover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13printf" +
"loat\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\x15prin" +
"tstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printif" +
"ace\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\x19co" +
"ncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstr" +
"ing5\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\x0feqstri" +
"ng\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!slic" +
"ebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebytetos" +
"tringtmp\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!stringt" +
"oslicerune\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\r" +
"retv·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\x1dslicestring" +
"copy\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>p\x00\x00>\vbuf·4\x00\x00\x02:l\x00\x00\t\rc" +
"onvT2I\x00\x06\x17\"\vtab·2\x00\x00>p\x00\x00>t\x00\x00\x02:l\x00\x00\t\x11assert" +
"E2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00>\vret·3\x00\x00\x00\t" +
"\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00>\vret·4\x00\x00" +
"\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assert" +
"E2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertE2T\x00\x06\x17\"|" +
"|\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01" +
"\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13asse" +
"rtI2E2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2I\x00\x06\x17" +
"\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertI2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>" +
"\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13as" +
"sertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x17panicdotty" +
"pe\x00\x06\x17\"\rhave·1\x00\x00\x9a\x01\rwant·2\x00\x00\x9a\x01\x84\x01\x00\x00\x00\t\rifa" +
"ceeq\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\rmakemap\x00\b\x17\"\x13mapType·2\x00" +
"\x00\n\rhint·3\x00\x00>\x11mapbuf·4\x00\x00>\x17bucketbuf·5\x00" +
"\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapaccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rh" +
"map·3\x00\x00>\vkey·4\x00\x00\x02>\vval·1\x00\x00\t!mapaccess" +
"1_fast32\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t!mapa" +
"ccess1_fast64\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t" +
"#mapaccess1_faststr\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02" +
">\xbc\x01\x00\x00\t\x1bmapaccess1_fat\x00\b\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00>\xba\x01\x00" +
"\x00\x17\"\rzero·5\x00\x00\x02>\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapT" +
"ype·3\x00\x00\x1d::\rhmap·4\x00\x00>\vkey·5\x00\x00\x04>\xbc\x01\x00\x00\x00\rp" +
"res·2\x00\x00\t!mapaccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01" +
"\x00\x00:\xce\x01\x00\x00\x04>\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>\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>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t" +
"\x1bmapaccess2_fat\x00\b\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00>\xce\x01\x00\x00\x17\"\rze" +
"ro·6\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapTy" +
"pe·1\x00\x00\x1d::\rhmap·2\x00\x00>\vkey·3\x00\x00>\vval·4\x00\x00" +
"\x00\t\x15mapiterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\x0fhiter·3\x00" +
"\x00\x00\t\x11mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\xe2\x01\x00\x00\x00\t\x15mapi" +
"ternext\x00\x02>\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15cha" +
"nType·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv" +
"1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00>p\x00\x00\x00\t\x11" +
"chanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00>\relem·4" +
"\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf8\x01\x00\x00\x1f\x04:\xfa\x01\x00\x00>p\x00\x00\x00\t\x11cl" +
"osechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renabled" +
"\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04>" +
"\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"||" +
"\x00\x00>\vdst·2\x00\x00>\vsrc·3\x00\x00\x00\t\x1btypedslicecopy\x00" +
"\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selectnbs" +
"end\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x01\x00\x00\t\x17selectnbrecv" +
"\x00\x06\x17\"\xf2\x01\x00\x00>p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19selectnbr" +
"ecv2\x00\b\x17\"\xf2\x01\x00\x00>p\x00\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhcha" +
"n·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsi" +
"ze·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel\xc2" +
"\xb72\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13select" +
"recv\x00\x06\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t\x15selectre" +
"cv2\x00\b\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00>\x80\x02\x00\x00\xf8\x01\x15received·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\x0fsele" +
"ctgo\x00\x02\x17\"\xae\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\b\x00" +
"\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x11grows" +
"lice\x00\x06\x17\"\b\x00\x00\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:\xcc\x02\x00\x00\t\rmemm" +
"ove\x00\x06>\tto·1\x00\x00>\vfrm·2\x00\x00\x16\x11length·3\x00d\x00\t\v" +
"memclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length·2\x00d\x00\t\x0fmemeq" +
"ual\x00\x06>\ax·2\x00\x00>\ay·3\x00\x00\x16\rsize·4\x00d\x01\x00\x00\t\x11mem" +
"equal8\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04>\xe2\x02\x00\x00" +
">\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13mem" +
"equal64\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04>\xe2\x02" +
"\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div" +
"\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00" +
"\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64" +
"touint64\x00\x01\x1a\x00\x01\x14\x00\t\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1d" +
"uint64tofloat64\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\x19racefunc" +
"enter\x00\x01\x16d\x00\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01\x16" +
"d\x00\t\x11racewrite\x00\x01\x16d\x00\t\x19racereadrange\x00\x04\x16\radd" +
"r·1\x00d\x16\rsize·2\x00d\x00\t\x1bracewriterange\x00\x04\x16\x94\x03\x00" +
"d\x16\x96\x03\x00d\x00\t\x0fmsanread\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x11msanwrit" +
"e\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\v\xf4\x01\x02\v\x00\x01\x00\n$$\n"
"cn\x00\x03v0\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"
const unsafeimport = "" +
"c\x00\x03v0\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"
"cn\x00\x03v0\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"

View file

@ -518,8 +518,7 @@ func cgen_wb(n, res *Node, wb bool) {
case ODOT,
ODOTPTR,
OINDEX,
OIND,
ONAME: // PHEAP or PPARAMREF var
OIND:
var n1 Node
Igen(n, &n1, res)
@ -1545,6 +1544,7 @@ func Agen(n *Node, res *Node) {
switch n.Op {
default:
Dump("bad agen", n)
Fatalf("agen: unknown op %v", Nconv(n, FmtShort|FmtSign))
case OCALLMETH:
@ -1571,24 +1571,6 @@ func Agen(n *Node, res *Node) {
Thearch.Gmove(&n1, res)
Regfree(&n1)
case ONAME:
// should only get here with names in this func.
if n.Name.Funcdepth > 0 && n.Name.Funcdepth != Funcdepth {
Dump("bad agen", n)
Fatalf("agen: bad ONAME funcdepth %d != %d", n.Name.Funcdepth, Funcdepth)
}
// should only get here for heap vars or paramref
if n.Class&PHEAP == 0 && n.Class != PPARAMREF {
Dump("bad agen", n)
Fatalf("agen: bad ONAME class %#x", n.Class)
}
Cgen(n.Name.Heapaddr, res)
if n.Xoffset != 0 {
addOffset(res, n.Xoffset)
}
case OIND:
Cgen(nl, res)
if !nl.NonNil {
@ -1646,8 +1628,9 @@ func Igen(n *Node, a *Node, res *Node) {
switch n.Op {
case ONAME:
if (n.Class&PHEAP != 0) || n.Class == PPARAMREF {
break
if n.Class == PAUTOHEAP {
Dump("igen", n)
Fatalf("bad name")
}
*a = *n
return
@ -1702,11 +1685,11 @@ func Igen(n *Node, a *Node, res *Node) {
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.
case OINDEX:
if n.Left.Type.IsArray() || (n.Left.Type.IsPtr() && n.Left.Left.Type.IsArray()) {
if Isconst(n.Right, CTINT) {
// Compute &a.

View file

@ -66,8 +66,39 @@ func closurebody(body []*Node) *Node {
// unhook them.
// make the list of pointers for the closure call.
for _, v := range func_.Func.Cvars.Slice() {
v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
v.Name.Param.Outerexpr = oldname(v.Sym)
// Unlink from v1; see comment in syntax.go type Param for these fields.
v1 := v.Name.Defn
v1.Name.Param.Innermost = v.Name.Param.Outer
// If the closure usage of v is not dense,
// we need to make it dense; now that we're out
// of the function in which v appeared,
// look up v.Sym in the enclosing function
// and keep it around for use in the compiled code.
//
// That is, suppose we just finished parsing the innermost
// closure f4 in this code:
//
// func f() {
// v := 1
// func() { // f2
// use(v)
// func() { // f3
// func() { // f4
// use(v)
// }()
// }()
// }()
// }
//
// At this point v.Outer is f2's v; there is no f3's v.
// To construct the closure f4 from within f3,
// we need to use f3's v and in this case we need to create f3's v.
// We are now in the context of f3, so calling oldname(v.Sym)
// obtains f3's v, creating it if necessary (as it is in the example).
//
// capturevars will decide whether to use v directly or &v.
v.Name.Param.Outer = oldname(v.Sym)
}
return func_
@ -75,7 +106,7 @@ func closurebody(body []*Node) *Node {
func typecheckclosure(func_ *Node, top int) {
for _, ln := range func_.Func.Cvars.Slice() {
n := ln.Name.Param.Closure
n := ln.Name.Defn
if !n.Name.Captured {
n.Name.Captured = true
if n.Name.Decldepth == 0 {
@ -215,8 +246,6 @@ func makeclosure(func_ *Node) *Node {
// We use value capturing for values <= 128 bytes that are never reassigned
// after capturing (effectively constant).
func capturevars(xfunc *Node) {
var outer *Node
lno := lineno
lineno = xfunc.Lineno
@ -239,14 +268,14 @@ func capturevars(xfunc *Node) {
// so that the outer frame also grabs them and knows they escape.
dowidth(v.Type)
outer = v.Name.Param.Outerexpr
v.Name.Param.Outerexpr = nil
outer := v.Name.Param.Outer
outermost := v.Name.Defn
// out parameters will be assigned to implicitly upon return.
if outer.Class != PPARAMOUT && !v.Name.Param.Closure.Addrtaken && !v.Name.Param.Closure.Assigned && v.Type.Width <= 128 {
if outer.Class != PPARAMOUT && !outermost.Addrtaken && !outermost.Assigned && v.Type.Width <= 128 {
v.Name.Byval = true
} else {
v.Name.Param.Closure.Addrtaken = true
outermost.Addrtaken = true
outer = Nod(OADDR, outer, nil)
}
@ -259,7 +288,7 @@ func capturevars(xfunc *Node) {
if v.Name.Byval {
how = "value"
}
Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Name.Param.Closure.Addrtaken, v.Name.Param.Closure.Assigned, int32(v.Type.Width))
Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Addrtaken, outermost.Assigned, int32(v.Type.Width))
}
outer = typecheck(outer, Erv)
@ -303,7 +332,7 @@ func transformclosure(xfunc *Node) {
continue
}
fld := newField()
fld.Funarg = true
fld.Funarg = FunargParams
if v.Name.Byval {
// If v is captured by value, we merely downgrade it to PPARAM.
v.Class = PPARAM
@ -313,7 +342,7 @@ func transformclosure(xfunc *Node) {
} else {
// If v of type T is captured by reference,
// we introduce function param &v *T
// and v remains PPARAMREF with &v heapaddr
// and v remains PAUTOHEAP with &v heapaddr
// (accesses will implicitly deref &v).
addr := newname(Lookupf("&%s", v.Sym.Name))
addr.Type = Ptrto(v.Type)

File diff suppressed because it is too large Load diff

View file

@ -405,7 +405,6 @@ func Complexgen(n *Node, res *Node) {
ODOTPTR,
OINDEX,
OIND,
ONAME, // PHEAP or PPARAMREF var
OCALLFUNC,
OCALLMETH,
OCALLINTER:

View file

@ -385,32 +385,36 @@ func oldname(s *Sym) *Node {
}
if Curfn != nil && n.Op == ONAME && n.Name.Funcdepth > 0 && n.Name.Funcdepth != Funcdepth {
// inner func is referring to var in outer func.
// Inner func is referring to var in outer func.
//
// TODO(rsc): If there is an outer variable x and we
// are parsing x := 5 inside the closure, until we get to
// the := it looks like a reference to the outer x so we'll
// make x a closure variable unnecessarily.
if n.Name.Param.Closure == nil || n.Name.Param.Closure.Name.Funcdepth != Funcdepth {
// create new closure var.
c := Nod(ONAME, nil, nil)
c := n.Name.Param.Innermost
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.Sym = s
c.Class = PPARAMREF
c.Class = PAUTOHEAP
c.setIsClosureVar(true)
c.Isddd = n.Isddd
c.Name.Defn = n
c.Addable = false
c.Ullman = 2
c.Name.Funcdepth = Funcdepth
c.Name.Param.Outer = n.Name.Param.Closure
n.Name.Param.Closure = c
c.Name.Param.Closure = n
// Link into list of active closure variables.
// Popped from list in func closurebody.
c.Name.Param.Outer = n.Name.Param.Innermost
n.Name.Param.Innermost = c
c.Xoffset = 0
Curfn.Func.Cvars.Append(c)
}
// return ref to closure var, not original
return n.Name.Param.Closure
return c
}
return n
@ -504,10 +508,8 @@ func ifacedcl(n *Node) {
n.Func = new(Func)
n.Func.FCurfn = Curfn
dclcontext = PPARAM
markdcl()
Funcdepth++
n.Func.Outer = Curfn
Curfn = n
funcstart(n)
funcargs(n.Right)
// funcbody is normally called after the parser has
@ -534,11 +536,7 @@ func funchdr(n *Node) {
}
dclcontext = PAUTO
markdcl()
Funcdepth++
n.Func.Outer = Curfn
Curfn = n
funcstart(n)
if n.Func.Nname != nil {
funcargs(n.Func.Nname.Name.Param.Ntype)
@ -671,6 +669,18 @@ 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
// start the function.
// called before funcargs; undone at end of funcbody.
func funcstart(n *Node) {
markdcl()
funcstack = append(funcstack, Curfn)
Funcdepth++
Curfn = n
}
// finish the body.
// called in auto-declaration context.
// returns in extern-declaration context.
@ -680,9 +690,8 @@ func funcbody(n *Node) {
Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
}
popdcl()
funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1]
Funcdepth--
Curfn = n.Func.Outer
n.Func.Outer = nil
if Funcdepth == 0 {
dclcontext = PEXTERN
}
@ -827,14 +836,14 @@ func tostruct0(t *Type, l []*Node) {
}
}
func tofunargs(l []*Node) *Type {
func tofunargs(l []*Node, funarg Funarg) *Type {
t := typ(TSTRUCT)
t.StructType().Funarg = true
t.StructType().Funarg = funarg
fields := make([]*Field, len(l))
for i, n := range l {
f := structfield(n)
f.Funarg = true
f.Funarg = funarg
// esc.go needs to find f given a PPARAM to add the tag.
if n.Left != nil && n.Left.Class == PPARAM {
@ -1025,9 +1034,9 @@ func functype0(t *Type, this *Node, in, out []*Node) {
if this != nil {
rcvr = []*Node{this}
}
*t.RecvsP() = tofunargs(rcvr)
*t.ResultsP() = tofunargs(out)
*t.ParamsP() = tofunargs(in)
*t.RecvsP() = tofunargs(rcvr, FunargRcvr)
*t.ResultsP() = tofunargs(out, FunargResults)
*t.ParamsP() = tofunargs(in, FunargParams)
checkdupfields("argument", t.Recvs(), t.Results(), t.Params())

View file

@ -640,7 +640,7 @@ func esc(e *EscState, n *Node, up *Node) {
// "Big" conditions that were scattered around in walk have been gathered here
if n.Esc != EscHeap && n.Type != nil &&
(n.Type.Width > MaxStackVarSize ||
n.Op == ONEW && n.Type.Elem().Width >= 1<<16 ||
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= 1<<16 ||
n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
if Debug['m'] > 2 {
Warnl(n.Lineno, "%v is too large for stack", n)
@ -900,13 +900,13 @@ func esc(e *EscState, n *Node, up *Node) {
escassignSinkNilWhy(e, n, n7.Right, "map literal value")
}
// Link addresses of captured variables to closure.
case OCLOSURE:
// Link addresses of captured variables to closure.
for _, v := range n.Func.Cvars.Slice() {
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
continue
}
a := v.Name.Param.Closure
a := v.Name.Defn
if !v.Name.Byval {
a = Nod(OADDR, a, nil)
a.Lineno = v.Lineno
@ -1068,7 +1068,6 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
OIND, // dst = *x
ODOTPTR, // dst = (*x).f
ONAME,
OPARAM,
ODDDARG,
OPTRLIT,
OARRAYLIT,
@ -1818,14 +1817,14 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
}
}
// Treat a PPARAMREF closure variable as equivalent to the
// Treat a captured closure variable as equivalent to the
// original variable.
if src.Class == PPARAMREF {
if src.isClosureVar() {
if leaks && Debug['m'] != 0 {
Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort))
step.describe(src)
}
escwalk(e, level, dst, src.Name.Param.Closure, e.stepWalk(dst, src.Name.Param.Closure, "closure-var", step))
escwalk(e, level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step))
}
case OPTRLIT, OADDR:
@ -1835,20 +1834,20 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
}
if leaks {
src.Esc = EscHeap
addrescapes(src.Left)
if Debug['m'] != 0 && osrcesc != src.Esc {
p := src
if p.Left.Op == OCLOSURE {
p = p.Left // merely to satisfy error messages in tests
}
if Debug['m'] > 2 {
Warnl(src.Lineno, "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v",
Nconv(p, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth)
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)
} else {
Warnl(src.Lineno, "%v escapes to heap", Nconv(p, FmtShort))
step.describe(src)
}
}
addrescapes(src.Left)
escwalkBody(e, 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 {

View file

@ -121,7 +121,7 @@ func reexportdep(n *Node) {
//print("reexportdep %+hN\n", n);
switch n.Op {
case ONAME:
switch n.Class &^ PHEAP {
switch n.Class {
// methods will be printed along with their type
// nodes for T.Method expressions
case PFUNC:

View file

@ -218,9 +218,9 @@ var classnames = []string{
"Pxxx",
"PEXTERN",
"PAUTO",
"PAUTOHEAP",
"PPARAM",
"PPARAMOUT",
"PPARAMREF",
"PFUNC",
}
@ -251,14 +251,10 @@ func jconv(n *Node, flag FmtFlag) string {
}
if n.Class != 0 {
s := ""
if n.Class&PHEAP != 0 {
s = ",heap"
}
if int(n.Class&^PHEAP) < len(classnames) {
fmt.Fprintf(&buf, " class(%s%s)", classnames[n.Class&^PHEAP], s)
if int(n.Class) < len(classnames) {
fmt.Fprintf(&buf, " class(%s)", classnames[n.Class])
} else {
fmt.Fprintf(&buf, " class(%d?%s)", n.Class&^PHEAP, s)
fmt.Fprintf(&buf, " class(%d?)", n.Class)
}
}
@ -798,8 +794,8 @@ func stmtfmt(n *Node) string {
switch n.Op {
case ODCL:
if fmtmode == FExp {
switch n.Left.Class &^ PHEAP {
case PPARAM, PPARAMOUT, PAUTO:
switch n.Left.Class {
case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP:
f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type)
goto ret
}
@ -1197,7 +1193,7 @@ func exprfmt(n *Node, prec int) string {
if n.Nbody.Len() != 0 {
return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
}
return fmt.Sprintf("%v { %v }", n.Type, n.Name.Param.Closure.Nbody)
return fmt.Sprintf("%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()
@ -1663,7 +1659,7 @@ func Fldconv(f *Field, flag FmtFlag) string {
}
if s != nil && f.Embedded == 0 {
if f.Funarg {
if f.Funarg != FunargNone {
name = Nconv(f.Nname, 0)
} else if flag&FmtLong != 0 {
name = sconv(s, FmtShort|FmtByte) // qualify non-exported names (used on structs, not on funarg)
@ -1696,7 +1692,7 @@ func Fldconv(f *Field, flag FmtFlag) string {
// (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) && f.Note != "" {
if flag&FmtShort == 0 && (!fmtbody || f.Funarg == FunargNone) && f.Note != "" {
str += " " + strconv.Quote(f.Note)
}

View file

@ -43,52 +43,38 @@ func addrescapes(n *Node) {
break
}
switch n.Class {
case PPARAMREF:
// If a closure reference escapes, mark the outer variable as escaping.
if n.isClosureVar() {
addrescapes(n.Name.Defn)
// if func param, need separate temporary
// to hold heap pointer.
// the function type has already been checked
// (we're in the function body)
// so the param already has a valid xoffset.
// expression to refer to stack copy
case PPARAM, PPARAMOUT:
n.Name.Param.Stackparam = Nod(OPARAM, n, nil)
n.Name.Param.Stackparam.Type = n.Type
n.Name.Param.Stackparam.Addable = true
if n.Xoffset == BADWIDTH {
Fatalf("addrescapes before param assignment")
break
}
n.Name.Param.Stackparam.Xoffset = n.Xoffset
fallthrough
case PAUTO:
n.Class |= PHEAP
if n.Class != PPARAM && n.Class != PPARAMOUT && n.Class != PAUTO {
break
}
n.Addable = false
n.Ullman = 2
n.Xoffset = 0
// create stack variable to hold pointer to heap
// This is a plain parameter or local variable that needs to move to the heap,
// but possibly for the function outside the one we're compiling.
// That is, if we have:
//
// func f(x int) {
// func() {
// global = &x
// }
// }
//
// then we're analyzing the inner closure but we need to move x to the
// heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
oldfn := Curfn
Curfn = n.Name.Curfn
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
Curfn = Curfn.Func.Closure
}
n.Name.Heapaddr = temp(Ptrto(n.Type))
buf := fmt.Sprintf("&%v", n.Sym)
n.Name.Heapaddr.Sym = Lookup(buf)
n.Name.Heapaddr.Orig.Sym = n.Name.Heapaddr.Sym
n.Esc = EscHeap
if Debug['m'] != 0 {
fmt.Printf("%v: moved to heap: %v\n", n.Line(), n)
}
ln := lineno
lineno = Curfn.Lineno
moveToHeap(n)
Curfn = oldfn
}
lineno = ln
case OIND, ODOTPTR:
break
@ -105,6 +91,110 @@ func addrescapes(n *Node) {
}
}
// isParamStackCopy reports whether this is the on-stack copy of a
// function parameter that moved to the heap.
func (n *Node) isParamStackCopy() bool {
return n.Op == ONAME && (n.Class == PPARAM || n.Class == PPARAMOUT) && n.Name.Heapaddr != nil
}
// isParamHeapCopy reports whether this is the on-heap copy of
// a function parameter that moved to the heap.
func (n *Node) isParamHeapCopy() bool {
return n.Op == ONAME && n.Class == PAUTOHEAP && n.Name.Param.Stackcopy != nil
}
// 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)
}
if n.Class == PAUTOHEAP {
Dump("n", n)
Fatalf("double move to heap")
}
// Allocate a local stack variable to hold the pointer to the heap copy.
// temp will add it to the function declaration list automatically.
heapaddr := temp(Ptrto(n.Type))
heapaddr.Sym = Lookup("&" + n.Sym.Name)
heapaddr.Orig.Sym = heapaddr.Sym
// Parameters have a local stack copy used at function start/end
// in addition to the copy in the heap that may live longer than
// the function.
if n.Class == PPARAM || n.Class == PPARAMOUT {
if n.Xoffset == BADWIDTH {
Fatalf("addrescapes before param assignment")
}
// We rewrite n below to be a heap variable (indirection of heapaddr).
// Preserve a copy so we can still write code referring to the original,
// and substitute that copy into the function declaration list
// so that analyses of the local (on-stack) variables use it.
stackcopy := 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)
}
n.Name.Param.Stackcopy = stackcopy
// Substitute the stackcopy into the function variable list so that
// liveness and other analyses use the underlying stack slot
// and not the now-pseudo-variable n.
found := false
for i, d := range Curfn.Func.Dcl {
if d == n {
Curfn.Func.Dcl[i] = stackcopy
found = true
break
}
// Parameters are before locals, so can stop early.
// This limits the search even in functions with many local variables.
if d.Class == PAUTO {
break
}
}
if !found {
Fatalf("cannot find %v in local variable list", n)
}
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
}
// Modify n in place so that uses of n now mean indirection of the heapaddr.
n.Class = PAUTOHEAP
n.Ullman = 2
n.Xoffset = 0
n.Name.Heapaddr = heapaddr
n.Esc = EscHeap
if Debug['m'] != 0 {
fmt.Printf("%v: moved to heap: %v\n", n.Line(), n)
}
}
func clearlabels() {
for _, l := range labellist {
l.Sym.Label = nil
@ -243,16 +333,9 @@ func cgen_dcl(n *Node) {
Fatalf("cgen_dcl")
}
if n.Class&PHEAP == 0 {
return
if n.Class == PAUTOHEAP {
Fatalf("cgen_dcl %v", n)
}
if compiling_runtime {
Yyerror("%v escapes to heap, not allowed in runtime.", n)
}
if prealloc[n] == nil {
prealloc[n] = callnew(n.Type)
}
Cgen_as(n.Name.Heapaddr, prealloc[n])
}
// generate discard of value
@ -263,7 +346,7 @@ func cgen_discard(nr *Node) {
switch nr.Op {
case ONAME:
if nr.Class&PHEAP == 0 && nr.Class != PEXTERN && nr.Class != PFUNC && nr.Class != PPARAMREF {
if nr.Class != PAUTOHEAP && nr.Class != PEXTERN && nr.Class != PFUNC {
gused(nr)
}
@ -908,11 +991,6 @@ func Cgen_as_wb(nl, nr *Node, wb bool) {
}
if nr == nil || iszero(nr) {
// heaps should already be clear
if nr == nil && (nl.Class&PHEAP != 0) {
return
}
tl := nl.Type
if tl == nil {
return

View file

@ -91,14 +91,12 @@ const (
Pxxx Class = iota
PEXTERN // global variable
PAUTO // local variables
PAUTOHEAP // local variable or parameter moved to heap
PPARAM // input arguments
PPARAMOUT // output results
PPARAMREF // closure variable reference
PFUNC // global function
PDISCARD // discard during parse of duplicate import
PHEAP = 1 << 7 // an extra bit to identify an escaped variable
)
// note this is the runtime representation
@ -133,6 +131,7 @@ var pragcgobuf string
var infile string
var outfile string
var linkobj string
var bout *bio.Writer
@ -260,8 +259,6 @@ var Widthreg int
var nblank *Node
var Funcdepth int32
var typecheckok bool
var compiling_runtime bool

View file

@ -53,7 +53,6 @@ func Ismem(n *Node) bool {
OCAP,
OINDREG,
ONAME,
OPARAM,
OCLOSUREVAR:
return true
@ -349,18 +348,6 @@ func Naddr(a *obj.Addr, n *Node) {
a.Width = 0
}
// n->left is PHEAP ONAME for stack parameter.
// compute address of actual parameter on stack.
case OPARAM:
a.Etype = uint8(Simtype[n.Left.Type.Etype])
a.Width = n.Left.Type.Width
a.Offset = n.Xoffset
a.Sym = Linksym(n.Left.Sym)
a.Type = obj.TYPE_MEM
a.Name = obj.NAME_PARAM
a.Node = n.Left.Orig
case OCLOSUREVAR:
if !Curfn.Func.Needctxt {
Fatalf("closurevar without needctxt")
@ -528,25 +515,36 @@ func newplist() *obj.Plist {
return pl
}
// nodarg does something that depends on the value of
// fp (this was previously completely undocumented).
// nodarg returns a Node for the function argument denoted by t,
// which is either the entire function argument or result struct (t is a struct *Type)
// or a specific argument (t is a *Field within a struct *Type).
//
// fp=1 corresponds to input args
// fp=0 corresponds to output args
// fp=-1 is a special case of output args for a
// specific call from walk that previously (and
// incorrectly) passed a 1; the behavior is exactly
// the same as it is for 1, except that PARAMOUT is
// generated instead of PARAM.
// If fp is 0, the node is for use by a caller invoking the given
// function, preparing the arguments before the call
// or retrieving the results after the call.
// In this case, the node will correspond to an outgoing argument
// slot like 8(SP).
//
// If fp is 1, the node is for use by the function itself
// (the callee), to retrieve its arguments or write its results.
// In this case the node will be an ONAME with an appropriate
// type and offset.
func nodarg(t interface{}, fp int) *Node {
var n *Node
var funarg Funarg
switch t := t.(type) {
default:
Fatalf("bad nodarg %T(%v)", t, t)
case *Type:
// entire argument struct, not just one arg
// Entire argument struct, not just one arg
if !t.IsFuncArgStruct() {
Fatalf("nodarg: bad type %v", t)
}
funarg = t.StructType().Funarg
// Build fake variable name for whole arg struct.
n = Nod(ONAME, nil, nil)
n.Sym = Lookup(".args")
n.Type = t
@ -559,15 +557,43 @@ func nodarg(t interface{}, fp int) *Node {
}
n.Xoffset = first.Offset
n.Addable = true
case *Field:
if fp == 1 || fp == -1 {
funarg = t.Funarg
if fp == 1 {
// NOTE(rsc): This should be using t.Nname directly,
// except in the case where t.Nname.Sym is the blank symbol and
// so the assignment would be discarded during code generation.
// In that case we need to make a new node, and there is no harm
// in optimization passes to doing so. But otherwise we should
// definitely be using the actual declaration and not a newly built node.
// The extra Fatalf checks here are verifying that this is the case,
// without changing the actual logic (at time of writing, it's getting
// toward time for the Go 1.7 beta).
// At some quieter time (assuming we've never seen these Fatalfs happen)
// we could change this code to use "expect" directly.
expect := t.Nname
if expect.isParamHeapCopy() {
expect = expect.Name.Param.Stackcopy
}
for _, n := range Curfn.Func.Dcl {
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
if n != expect {
Fatalf("nodarg: unexpected node: %v (%p %v) vs %v (%p %v)", n, n, n.Op, t.Nname, t.Nname, t.Nname.Op)
}
return n
}
}
if !isblanksym(expect.Sym) {
Fatalf("nodarg: did not find node in dcl list: %v", expect)
}
}
// 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.Type = t.Type
n.Sym = t.Sym
@ -577,8 +603,6 @@ func nodarg(t interface{}, fp int) *Node {
n.Xoffset = t.Offset
n.Addable = true
n.Orig = t.Nname
default:
panic("unreachable")
}
// Rewrite argument named _ to __,
@ -589,23 +613,23 @@ func nodarg(t interface{}, fp int) *Node {
}
switch fp {
case 0: // output arg
n.Op = OINDREG
default:
Fatalf("bad fp")
case 0: // preparing arguments for call
n.Op = OINDREG
n.Reg = int16(Thearch.REGSP)
n.Xoffset += Ctxt.FixedFrameSize()
case 1: // input arg
case 1: // reading arguments inside call
n.Class = PPARAM
case -1: // output arg from paramstoheap
if funarg == FunargResults {
n.Class = PPARAMOUT
case 2: // offset output arg
Fatalf("shouldn't be used")
}
}
n.Typecheck = 1
n.Addrtaken = true // keep optimizers at bay
return n
}

View file

@ -32,19 +32,20 @@ func renameinit() *Sym {
// hand-craft the following initialization code
// var initdone· uint8 (1)
// func init() (2)
// func init() { (2)
// if initdone· > 1 { (3)
// return (3a)
// if initdone· == 1 { (4)
// throw(); (4a)
// }
// initdone· = 1; (6)
// if initdone· == 1 { (4)
// throw() (4a)
// }
// initdone· = 1 (5)
// // over all matching imported symbols
// <pkg>.init() (7)
// { <init stmts> } (8)
// init.<n>() // if any (9)
// initdone· = 2; (10)
// return (11)
// <pkg>.init() (6)
// { <init stmts> } (7)
// init.<n>() // if any (8)
// initdone· = 2 (9)
// return (10)
// }
func anyinit(n []*Node) bool {
// are there any interesting init statements
@ -132,12 +133,12 @@ func fninit(n []*Node) {
// (4a)
b.Nbody.Set1(Nod(OCALL, syslook("throwinit"), nil))
// (6)
// (5)
a = Nod(OAS, gatevar, Nodintconst(1))
r = append(r, a)
// (7)
// (6)
for _, s := range initSyms {
if s.Def != nil && s != initsym {
// could check that it is fn of no args/returns
@ -146,10 +147,10 @@ func fninit(n []*Node) {
}
}
// (8)
// (7)
r = append(r, nf...)
// (9)
// (8)
// could check that it is fn of no args/returns
for i := 1; ; i++ {
s := LookupN("init.", i)
@ -160,12 +161,12 @@ func fninit(n []*Node) {
r = append(r, a)
}
// (10)
// (9)
a = Nod(OAS, gatevar, Nodintconst(2))
r = append(r, a)
// (11)
// (10)
a = Nod(ORETURN, nil, nil)
r = append(r, a)

View file

@ -27,9 +27,7 @@
package gc
import (
"fmt"
)
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.
@ -180,6 +178,7 @@ func ishairy(n *Node, budget *int32) bool {
*budget -= fn.InlCost
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 d := n.Left.Sym.Def; d != nil && d.Func.Inl.Len() != 0 {
*budget -= d.Func.InlCost
@ -568,14 +567,13 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
if ln.Class == PPARAMOUT { // return values handled below.
continue
}
if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
continue
}
if ln.Op == ONAME {
ln.Name.Inlvar = inlvar(ln)
// Typecheck because inlvar is not necessarily a function parameter.
ln.Name.Inlvar = typecheck(ln.Name.Inlvar, Erv)
if ln.Class&^PHEAP != PAUTO {
ninit.Append(Nod(ODCL, ln.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
ln.Name.Inlvar = 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))
}
}
}

View file

@ -0,0 +1,289 @@
package gc
import "testing"
// Tests to make sure logic simplification rules are correct.
func TestLogic64(t *testing.T) {
// test values to determine function equality
values := [...]int64{-1 << 63, 1<<63 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4}
// golden functions we use repeatedly
zero := func(x int64) int64 { return 0 }
id := func(x int64) int64 { return x }
or := func(x, y int64) int64 { return x | y }
and := func(x, y int64) int64 { return x & y }
y := func(x, y int64) int64 { return y }
for _, test := range [...]struct {
name string
f func(int64) int64
golden func(int64) int64
}{
{"x|x", func(x int64) int64 { return x | x }, id},
{"x|0", func(x int64) int64 { return x | 0 }, id},
{"x|-1", func(x int64) int64 { return x | -1 }, func(x int64) int64 { return -1 }},
{"x&x", func(x int64) int64 { return x & x }, id},
{"x&0", func(x int64) int64 { return x & 0 }, zero},
{"x&-1", func(x int64) int64 { return x & -1 }, id},
{"x^x", func(x int64) int64 { return x ^ x }, zero},
{"x^0", func(x int64) int64 { return x ^ 0 }, id},
{"x^-1", func(x int64) int64 { return x ^ -1 }, func(x int64) int64 { return ^x }},
{"x+0", func(x int64) int64 { return x + 0 }, id},
{"x-x", func(x int64) int64 { return x - x }, zero},
{"x*0", func(x int64) int64 { return x * 0 }, zero},
{"^^x", func(x int64) int64 { return ^^x }, id},
} {
for _, v := range values {
got := test.f(v)
want := test.golden(v)
if want != got {
t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want)
}
}
}
for _, test := range [...]struct {
name string
f func(int64, int64) int64
golden func(int64, int64) int64
}{
{"x|(x|y)", func(x, y int64) int64 { return x | (x | y) }, or},
{"x|(y|x)", func(x, y int64) int64 { return x | (y | x) }, or},
{"(x|y)|x", func(x, y int64) int64 { return (x | y) | x }, or},
{"(y|x)|x", func(x, y int64) int64 { return (y | x) | x }, or},
{"x&(x&y)", func(x, y int64) int64 { return x & (x & y) }, and},
{"x&(y&x)", func(x, y int64) int64 { return x & (y & x) }, and},
{"(x&y)&x", func(x, y int64) int64 { return (x & y) & x }, and},
{"(y&x)&x", func(x, y int64) int64 { return (y & x) & x }, and},
{"x^(x^y)", func(x, y int64) int64 { return x ^ (x ^ y) }, y},
{"x^(y^x)", func(x, y int64) int64 { return x ^ (y ^ x) }, y},
{"(x^y)^x", func(x, y int64) int64 { return (x ^ y) ^ x }, y},
{"(y^x)^x", func(x, y int64) int64 { return (y ^ x) ^ x }, y},
{"-(y-x)", func(x, y int64) int64 { return -(y - x) }, func(x, y int64) int64 { return x - y }},
{"(x+y)-x", func(x, y int64) int64 { return (x + y) - x }, y},
{"(y+x)-x", func(x, y int64) int64 { return (y + x) - x }, y},
} {
for _, v := range values {
for _, w := range values {
got := test.f(v, w)
want := test.golden(v, w)
if want != got {
t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want)
}
}
}
}
}
func TestLogic32(t *testing.T) {
// test values to determine function equality
values := [...]int32{-1 << 31, 1<<31 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4}
// golden functions we use repeatedly
zero := func(x int32) int32 { return 0 }
id := func(x int32) int32 { return x }
or := func(x, y int32) int32 { return x | y }
and := func(x, y int32) int32 { return x & y }
y := func(x, y int32) int32 { return y }
for _, test := range [...]struct {
name string
f func(int32) int32
golden func(int32) int32
}{
{"x|x", func(x int32) int32 { return x | x }, id},
{"x|0", func(x int32) int32 { return x | 0 }, id},
{"x|-1", func(x int32) int32 { return x | -1 }, func(x int32) int32 { return -1 }},
{"x&x", func(x int32) int32 { return x & x }, id},
{"x&0", func(x int32) int32 { return x & 0 }, zero},
{"x&-1", func(x int32) int32 { return x & -1 }, id},
{"x^x", func(x int32) int32 { return x ^ x }, zero},
{"x^0", func(x int32) int32 { return x ^ 0 }, id},
{"x^-1", func(x int32) int32 { return x ^ -1 }, func(x int32) int32 { return ^x }},
{"x+0", func(x int32) int32 { return x + 0 }, id},
{"x-x", func(x int32) int32 { return x - x }, zero},
{"x*0", func(x int32) int32 { return x * 0 }, zero},
{"^^x", func(x int32) int32 { return ^^x }, id},
} {
for _, v := range values {
got := test.f(v)
want := test.golden(v)
if want != got {
t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want)
}
}
}
for _, test := range [...]struct {
name string
f func(int32, int32) int32
golden func(int32, int32) int32
}{
{"x|(x|y)", func(x, y int32) int32 { return x | (x | y) }, or},
{"x|(y|x)", func(x, y int32) int32 { return x | (y | x) }, or},
{"(x|y)|x", func(x, y int32) int32 { return (x | y) | x }, or},
{"(y|x)|x", func(x, y int32) int32 { return (y | x) | x }, or},
{"x&(x&y)", func(x, y int32) int32 { return x & (x & y) }, and},
{"x&(y&x)", func(x, y int32) int32 { return x & (y & x) }, and},
{"(x&y)&x", func(x, y int32) int32 { return (x & y) & x }, and},
{"(y&x)&x", func(x, y int32) int32 { return (y & x) & x }, and},
{"x^(x^y)", func(x, y int32) int32 { return x ^ (x ^ y) }, y},
{"x^(y^x)", func(x, y int32) int32 { return x ^ (y ^ x) }, y},
{"(x^y)^x", func(x, y int32) int32 { return (x ^ y) ^ x }, y},
{"(y^x)^x", func(x, y int32) int32 { return (y ^ x) ^ x }, y},
{"-(y-x)", func(x, y int32) int32 { return -(y - x) }, func(x, y int32) int32 { return x - y }},
{"(x+y)-x", func(x, y int32) int32 { return (x + y) - x }, y},
{"(y+x)-x", func(x, y int32) int32 { return (y + x) - x }, y},
} {
for _, v := range values {
for _, w := range values {
got := test.f(v, w)
want := test.golden(v, w)
if want != got {
t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want)
}
}
}
}
}
func TestLogic16(t *testing.T) {
// test values to determine function equality
values := [...]int16{-1 << 15, 1<<15 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4}
// golden functions we use repeatedly
zero := func(x int16) int16 { return 0 }
id := func(x int16) int16 { return x }
or := func(x, y int16) int16 { return x | y }
and := func(x, y int16) int16 { return x & y }
y := func(x, y int16) int16 { return y }
for _, test := range [...]struct {
name string
f func(int16) int16
golden func(int16) int16
}{
{"x|x", func(x int16) int16 { return x | x }, id},
{"x|0", func(x int16) int16 { return x | 0 }, id},
{"x|-1", func(x int16) int16 { return x | -1 }, func(x int16) int16 { return -1 }},
{"x&x", func(x int16) int16 { return x & x }, id},
{"x&0", func(x int16) int16 { return x & 0 }, zero},
{"x&-1", func(x int16) int16 { return x & -1 }, id},
{"x^x", func(x int16) int16 { return x ^ x }, zero},
{"x^0", func(x int16) int16 { return x ^ 0 }, id},
{"x^-1", func(x int16) int16 { return x ^ -1 }, func(x int16) int16 { return ^x }},
{"x+0", func(x int16) int16 { return x + 0 }, id},
{"x-x", func(x int16) int16 { return x - x }, zero},
{"x*0", func(x int16) int16 { return x * 0 }, zero},
{"^^x", func(x int16) int16 { return ^^x }, id},
} {
for _, v := range values {
got := test.f(v)
want := test.golden(v)
if want != got {
t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want)
}
}
}
for _, test := range [...]struct {
name string
f func(int16, int16) int16
golden func(int16, int16) int16
}{
{"x|(x|y)", func(x, y int16) int16 { return x | (x | y) }, or},
{"x|(y|x)", func(x, y int16) int16 { return x | (y | x) }, or},
{"(x|y)|x", func(x, y int16) int16 { return (x | y) | x }, or},
{"(y|x)|x", func(x, y int16) int16 { return (y | x) | x }, or},
{"x&(x&y)", func(x, y int16) int16 { return x & (x & y) }, and},
{"x&(y&x)", func(x, y int16) int16 { return x & (y & x) }, and},
{"(x&y)&x", func(x, y int16) int16 { return (x & y) & x }, and},
{"(y&x)&x", func(x, y int16) int16 { return (y & x) & x }, and},
{"x^(x^y)", func(x, y int16) int16 { return x ^ (x ^ y) }, y},
{"x^(y^x)", func(x, y int16) int16 { return x ^ (y ^ x) }, y},
{"(x^y)^x", func(x, y int16) int16 { return (x ^ y) ^ x }, y},
{"(y^x)^x", func(x, y int16) int16 { return (y ^ x) ^ x }, y},
{"-(y-x)", func(x, y int16) int16 { return -(y - x) }, func(x, y int16) int16 { return x - y }},
{"(x+y)-x", func(x, y int16) int16 { return (x + y) - x }, y},
{"(y+x)-x", func(x, y int16) int16 { return (y + x) - x }, y},
} {
for _, v := range values {
for _, w := range values {
got := test.f(v, w)
want := test.golden(v, w)
if want != got {
t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want)
}
}
}
}
}
func TestLogic8(t *testing.T) {
// test values to determine function equality
values := [...]int8{-1 << 7, 1<<7 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4}
// golden functions we use repeatedly
zero := func(x int8) int8 { return 0 }
id := func(x int8) int8 { return x }
or := func(x, y int8) int8 { return x | y }
and := func(x, y int8) int8 { return x & y }
y := func(x, y int8) int8 { return y }
for _, test := range [...]struct {
name string
f func(int8) int8
golden func(int8) int8
}{
{"x|x", func(x int8) int8 { return x | x }, id},
{"x|0", func(x int8) int8 { return x | 0 }, id},
{"x|-1", func(x int8) int8 { return x | -1 }, func(x int8) int8 { return -1 }},
{"x&x", func(x int8) int8 { return x & x }, id},
{"x&0", func(x int8) int8 { return x & 0 }, zero},
{"x&-1", func(x int8) int8 { return x & -1 }, id},
{"x^x", func(x int8) int8 { return x ^ x }, zero},
{"x^0", func(x int8) int8 { return x ^ 0 }, id},
{"x^-1", func(x int8) int8 { return x ^ -1 }, func(x int8) int8 { return ^x }},
{"x+0", func(x int8) int8 { return x + 0 }, id},
{"x-x", func(x int8) int8 { return x - x }, zero},
{"x*0", func(x int8) int8 { return x * 0 }, zero},
{"^^x", func(x int8) int8 { return ^^x }, id},
} {
for _, v := range values {
got := test.f(v)
want := test.golden(v)
if want != got {
t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want)
}
}
}
for _, test := range [...]struct {
name string
f func(int8, int8) int8
golden func(int8, int8) int8
}{
{"x|(x|y)", func(x, y int8) int8 { return x | (x | y) }, or},
{"x|(y|x)", func(x, y int8) int8 { return x | (y | x) }, or},
{"(x|y)|x", func(x, y int8) int8 { return (x | y) | x }, or},
{"(y|x)|x", func(x, y int8) int8 { return (y | x) | x }, or},
{"x&(x&y)", func(x, y int8) int8 { return x & (x & y) }, and},
{"x&(y&x)", func(x, y int8) int8 { return x & (y & x) }, and},
{"(x&y)&x", func(x, y int8) int8 { return (x & y) & x }, and},
{"(y&x)&x", func(x, y int8) int8 { return (y & x) & x }, and},
{"x^(x^y)", func(x, y int8) int8 { return x ^ (x ^ y) }, y},
{"x^(y^x)", func(x, y int8) int8 { return x ^ (y ^ x) }, y},
{"(x^y)^x", func(x, y int8) int8 { return (x ^ y) ^ x }, y},
{"(y^x)^x", func(x, y int8) int8 { return (y ^ x) ^ x }, y},
{"-(y-x)", func(x, y int8) int8 { return -(y - x) }, func(x, y int8) int8 { return x - y }},
{"(x+y)-x", func(x, y int8) int8 { return (x + y) - x }, y},
{"(y+x)-x", func(x, y int8) int8 { return (y + x) - x }, y},
} {
for _, v := range values {
for _, w := range values {
got := test.f(v, w)
want := test.golden(v, w)
if want != got {
t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want)
}
}
}
}
}

View file

@ -178,6 +178,7 @@ func Main() {
flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
obj.Flagcount("l", "disable inlining", &Debug['l'])
flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
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")
@ -772,7 +773,7 @@ func importfile(f *Val, indent []byte) {
if p != "empty archive" {
if !strings.HasPrefix(p, "go object ") {
Yyerror("import %s: not a go object file", file)
Yyerror("import %s: not a go object file: %s", file, p)
errorexit()
}
@ -783,6 +784,21 @@ func importfile(f *Val, indent []byte) {
}
}
// process header lines
for {
p, err = imp.ReadString('\n')
if err != nil {
log.Fatalf("reading input: %v", err)
}
if p == "\n" {
break // header ends with blank line
}
if strings.HasPrefix(p, "safe") {
importpkg.Safe = true
break // ok to ignore rest
}
}
// assume files move (get installed)
// so don't record the full path.
linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib

View file

@ -8,6 +8,7 @@
// 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.
package main
import (

View file

@ -22,7 +22,34 @@ func formathdr(arhdr []byte, name string, size int64) {
copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
}
// These modes say which kind of object file to generate.
// The default use of the toolchain is to set both bits,
// generating a combined compiler+linker object, one that
// serves to describe the package to both the compiler and the linker.
// In fact the compiler and linker read nearly disjoint sections of
// that file, though, so in a distributed build setting it can be more
// efficient to split the output into two files, supplying the compiler
// object only to future compilations and the linker object only to
// future links.
//
// By default a combined object is written, but if -linkobj is specified
// on the command line then the default -o output is a compiler object
// and the -linkobj output is a linker object.
const (
modeCompilerObj = 1 << iota
modeLinkerObj
)
func dumpobj() {
if linkobj == "" {
dumpobj1(outfile, modeCompilerObj|modeLinkerObj)
} else {
dumpobj1(outfile, modeCompilerObj)
dumpobj1(linkobj, modeLinkerObj)
}
}
func dumpobj1(outfile string, mode int) {
var err error
bout, err = bio.Create(outfile)
if err != nil {
@ -40,8 +67,27 @@ func dumpobj() {
startobj = bout.Offset()
}
printheader := func() {
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
if buildid != "" {
fmt.Fprintf(bout, "build id %q\n", buildid)
}
if localpkg.Name == "main" {
fmt.Fprintf(bout, "main\n")
}
if safemode {
fmt.Fprintf(bout, "safe\n")
} else {
fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe"
}
fmt.Fprintf(bout, "\n") // header ends with blank line
}
printheader()
if mode&modeCompilerObj != 0 {
dumpexport()
}
if writearchive {
bout.Flush()
@ -53,12 +99,20 @@ func dumpobj() {
formathdr(arhdr[:], "__.PKGDEF", size)
bout.Write(arhdr[:])
bout.Flush()
bout.Seek(startobj+size+(size&1), 0)
}
if mode&modeLinkerObj == 0 {
bout.Close()
return
}
if writearchive {
// start object file
arhdr = [ArhdrSize]byte{}
bout.Write(arhdr[:])
startobj = bout.Offset()
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
printheader()
}
if pragcgobuf != "" {

View file

@ -76,7 +76,6 @@ var opnames = []string{
OINDEX: "INDEX",
OINDEXMAP: "INDEXMAP",
OKEY: "KEY",
OPARAM: "PARAM",
OLEN: "LEN",
OMAKE: "MAKE",
OMAKECHAN: "MAKECHAN",

View file

@ -1082,6 +1082,20 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
n.Left = orderaddrtemp(n.Left, order)
}
case OCONVNOP:
if n.Type.IsKind(TUNSAFEPTR) && n.Left.Type.IsKind(TUINTPTR) && (n.Left.Op == OCALLFUNC || n.Left.Op == OCALLINTER || n.Left.Op == OCALLMETH) {
// When reordering unsafe.Pointer(f()) into a separate
// statement, the conversion and function call must stay
// together. See golang.org/issue/15329.
orderinit(n.Left, order)
ordercall(n.Left, order)
if lhs == nil || lhs.Op != ONAME || instrumenting {
n = ordercopyexpr(n, n.Type, order, 0)
}
} else {
n.Left = orderexpr(n.Left, order, nil)
}
case OANDAND, OOROR:
mark := marktemp(order)
n.Left = orderexpr(n.Left, order, nil)

View file

@ -398,11 +398,8 @@ func (p *parser) import_package() {
p.import_error()
}
importsafe := false
// read but skip "safe" bit (see issue #15772)
if p.tok == LNAME {
if p.sym_.Name == "safe" {
importsafe = true
}
p.next()
}
p.want(';')
@ -413,7 +410,6 @@ func (p *parser) import_package() {
} else if importpkg.Name != name {
Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path)
}
importpkg.Safe = importsafe
typecheckok = true
defercheckwidth()

View file

@ -197,54 +197,41 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool {
return false
}
// Collects and returns a slice of *Nodes for functions arguments and local
// variables.
// livenessShouldTrack reports whether the liveness analysis
// should track the variable n.
// We don't care about variables that have no pointers,
// nor do we care about non-local variables,
// nor do we care about empty structs (handled by the pointer check),
// nor do we care about the fake PAUTOHEAP variables.
func livenessShouldTrack(n *Node) bool {
return n.Op == ONAME && (n.Class == PAUTO || n.Class == PPARAM || n.Class == PPARAMOUT) && haspointers(n.Type)
}
// getvariables returns the list of on-stack variables that we need to track.
func getvariables(fn *Node) []*Node {
var result []*Node
for _, ln := range fn.Func.Dcl {
if ln.Op == ONAME {
// In order for GODEBUG=gcdead=1 to work, each bitmap needs
// to contain information about all variables covered by the bitmap.
// For local variables, the bitmap only covers the stkptrsize
// bytes in the frame where variables containing pointers live.
// For arguments and results, the bitmap covers all variables,
// so we must include all the variables, even the ones without
// pointers.
//
var vars []*Node
for _, n := range fn.Func.Dcl {
if n.Op == ONAME {
// The Node.opt field is available for use by optimization passes.
// We use it to hold the index of the node in the variables array, plus 1
// (so that 0 means the Node is not in the variables array).
// Each pass should clear opt when done, but you never know,
// so clear them all ourselves too.
// We use it to hold the index of the node in the variables array
// (nil means the Node is not in the variables array).
// The Node.curfn field is supposed to be set to the current function
// already, but for some compiler-introduced names it seems not to be,
// so fix that here.
// Later, when we want to find the index of a node in the variables list,
// we will check that n.curfn == curfn and n.opt > 0. Then n.opt - 1
// we will check that n.Curfn == Curfn and n.Opt() != nil. Then n.Opt().(int32)
// is the index in the variables list.
ln.SetOpt(nil)
// The compiler doesn't emit initializations for zero-width parameters or results.
if ln.Type.Width == 0 {
continue
n.SetOpt(nil)
n.Name.Curfn = Curfn
}
ln.Name.Curfn = Curfn
switch ln.Class {
case PAUTO:
if haspointers(ln.Type) {
ln.SetOpt(int32(len(result)))
result = append(result, ln)
}
case PPARAM, PPARAMOUT:
ln.SetOpt(int32(len(result)))
result = append(result, ln)
}
if livenessShouldTrack(n) {
n.SetOpt(int32(len(vars)))
vars = append(vars, n)
}
}
return result
return vars
}
// A pretty printer for control flow graphs. Takes a slice of *BasicBlocks.
@ -567,9 +554,11 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
// read the out arguments - they won't be set until the new
// function runs.
for i, node := range vars {
switch node.Class &^ PHEAP {
switch node.Class {
case PPARAM:
if !node.NotLiveAtEnd() {
bvset(uevar, int32(i))
}
// If the result had its address taken, it is being tracked
// by the avarinit code, which does not use uevar.
@ -593,7 +582,7 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
// A text instruction marks the entry point to a function and
// the definition point of all in arguments.
for i, node := range vars {
switch node.Class &^ PHEAP {
switch node.Class {
case PPARAM:
if node.Addrtaken {
bvset(avarinit, int32(i))
@ -607,24 +596,17 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
from := &prog.From
if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Name.Curfn == Curfn {
switch ((from.Node).(*Node)).Class &^ PHEAP {
case PAUTO, PPARAM, PPARAMOUT:
pos, ok := from.Node.(*Node).Opt().(int32) // index in vars
if !ok {
break
}
if pos >= int32(len(vars)) || vars[pos] != from.Node {
Fatalf("bad bookkeeping in liveness %v %d", Nconv(from.Node.(*Node), 0), pos)
}
if ((from.Node).(*Node)).Addrtaken {
if from.Node != nil && from.Sym != nil {
n := from.Node.(*Node)
if pos := liveIndex(n, vars); pos >= 0 {
if n.Addrtaken {
bvset(avarinit, pos)
} else {
if prog.Info.Flags&(LeftRead|LeftAddr) != 0 {
bvset(uevar, pos)
}
if prog.Info.Flags&LeftWrite != 0 {
if from.Node != nil && !Isfat(((from.Node).(*Node)).Type) {
if !Isfat(n.Type) {
bvset(varkill, pos)
}
}
@ -635,17 +617,10 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
to := &prog.To
if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Name.Curfn == Curfn {
switch ((to.Node).(*Node)).Class &^ PHEAP {
case PAUTO, PPARAM, PPARAMOUT:
pos, ok := to.Node.(*Node).Opt().(int32) // index in vars
if !ok {
return
}
if pos >= int32(len(vars)) || vars[pos] != to.Node {
Fatalf("bad bookkeeping in liveness %v %d", Nconv(to.Node.(*Node), 0), pos)
}
if ((to.Node).(*Node)).Addrtaken {
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)
}
@ -665,7 +640,7 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
bvset(uevar, pos)
}
if prog.Info.Flags&RightWrite != 0 {
if to.Node != nil && (!Isfat(((to.Node).(*Node)).Type) || prog.As == obj.AVARDEF) {
if !Isfat(n.Type) || prog.As == obj.AVARDEF {
bvset(varkill, pos)
}
}
@ -675,6 +650,24 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
}
}
// liveIndex returns the index of n in the set of tracked vars.
// If n is not a tracked var, liveIndex returns -1.
// If n is not a tracked var but should be tracked, liveIndex crashes.
func liveIndex(n *Node, vars []*Node) int32 {
if n.Name.Curfn != Curfn || !livenessShouldTrack(n) {
return -1
}
pos, ok := n.Opt().(int32) // index in vars
if !ok {
Fatalf("lost track of variable in liveness: %v (%p, %p)", n, n, n.Orig)
}
if pos >= int32(len(vars)) || vars[pos] != n {
Fatalf("bad bookkeeping in liveness: %v (%p, %p)", n, n, n.Orig)
}
return pos
}
// Constructs a new liveness structure used to hold the global state of the
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
// vars argument is a slice of *Nodes.
@ -812,8 +805,7 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) {
return
}
for _, a := range fn.Func.Dcl {
class := a.Class &^ PHEAP
if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n {
if a.Op == ONAME && (a.Class == PPARAM || a.Class == PPARAMOUT) && a == n {
return
}
}
@ -980,23 +972,6 @@ func onebitlivepointermap(lv *Liveness, liveout bvec, vars []*Node, args bvec, l
onebitwalktype1(node.Type, &xoffset, args)
}
}
// The node list only contains declared names.
// If the receiver or arguments are unnamed, they will be omitted
// from the list above. Preserve those values - even though they are unused -
// in order to keep their addresses live for use in stack traces.
thisargtype := lv.fn.Type.Recvs()
if thisargtype != nil {
xoffset = 0
onebitwalktype1(thisargtype, &xoffset, args)
}
inargtype := lv.fn.Type.Params()
if inargtype != nil {
xoffset = 0
onebitwalktype1(inargtype, &xoffset, args)
}
}
// Construct a disembodied instruction.

View file

@ -419,7 +419,6 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
case OPRINT, // don't bother instrumenting it
OPRINTN, // don't bother instrumenting it
OCHECKNIL, // always followed by a read.
OPARAM, // it appears only in fn->exit to copy heap params back
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
@ -496,7 +495,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
// e.g. if we've got a local variable/method receiver
// that has got a pointer inside. Whether it points to
// the heap or not is impossible to know at compile time
if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND {
if class == PAUTOHEAP || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND {
hascalls := 0
foreach(n, hascallspred, &hascalls)
if hascalls != 0 {

View file

@ -516,7 +516,7 @@ func isliteral(n *Node) bool {
}
func (n *Node) isSimpleName() bool {
return n.Op == ONAME && n.Addable && n.Class&PHEAP == 0 && n.Class != PPARAMREF
return n.Op == ONAME && n.Addable && n.Class != PAUTOHEAP
}
func litas(l *Node, r *Node, init *Nodes) {

View file

@ -23,7 +23,7 @@ func TestSizeof(t *testing.T) {
_64bit uintptr // size on 64bit platforms
}{
{Flow{}, 52, 88},
{Func{}, 96, 168},
{Func{}, 92, 160},
{Name{}, 52, 80},
{Node{}, 92, 144},
{Sym{}, 60, 112},

View file

@ -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 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 defseen == nil {
defseen = x
}
if defseen != x || x == nil { // TODO: too conservative at loops, does better if x == nil -> continue
// 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())
}

View file

@ -161,23 +161,19 @@ func buildssa(fn *Node) *ssa.Func {
// the function.
s.returns = append(s.returns, n)
}
case PAUTO | PHEAP:
// TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition
aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n})
s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
case PPARAM | PHEAP, PPARAMOUT | PHEAP:
// This ends up wrong, have to do it at the PARAM node instead.
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.
case PAUTOHEAP:
// moved to heap - already handled by frontend
case PFUNC:
// local function - already handled by frontend
default:
str := ""
if n.Class&PHEAP != 0 {
str = ",heap"
}
s.Unimplementedf("local variable with class %s%s unimplemented", classnames[n.Class&^PHEAP], str)
s.Unimplementedf("local variable with class %s unimplemented", classnames[n.Class])
}
}
@ -218,8 +214,16 @@ 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()
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")
}
// Don't carry reference this around longer than necessary
s.exitCode = Nodes{}
@ -282,9 +286,13 @@ type state struct {
// list of FwdRef values.
fwdRefs []*ssa.Value
// list of PPARAMOUT (return) variables. Does not include PPARAM|PHEAP vars.
// 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
cgoUnsafeArgs bool
noWB bool
WBLineno int32 // line number of first write barrier. 0=no write barriers
@ -577,24 +585,9 @@ func (s *state) stmt(n *Node) {
return
case ODCL:
if n.Left.Class&PHEAP == 0 {
return
if n.Left.Class == PAUTOHEAP {
Fatalf("DCL %v", n)
}
if compiling_runtime {
Fatalf("%v escapes to heap, not allowed in runtime.", n)
}
// TODO: the old pass hides the details of PHEAP
// variables behind ONAME nodes. Figure out if it's better
// to rewrite the tree and make the heapaddr construct explicit
// or to keep this detail hidden behind the scenes.
palloc := prealloc[n.Left]
if palloc == nil {
palloc = callnew(n.Left.Type)
prealloc[n.Left] = palloc
}
r := s.expr(palloc)
s.assign(n.Left.Name.Heapaddr, r, false, false, n.Lineno, 0)
case OLABEL:
sym := n.Left.Sym
@ -980,8 +973,7 @@ func (s *state) exit() *ssa.Block {
// Store SSAable PPARAMOUT variables back to stack locations.
for _, n := range s.returns {
aux := &ssa.ArgSymbol{Typ: n.Type, Node: n}
addr := s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
addr := s.decladdrs[n]
val := s.variable(n, n.Type)
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, n, s.mem())
s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, n.Type.Size(), addr, val, s.mem())
@ -990,6 +982,16 @@ 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()
@ -1428,9 +1430,6 @@ func (s *state) expr(n *Node) *ssa.Value {
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)
case OPARAM:
addr := s.addr(n, false)
return s.newValue2(ssa.OpLoad, n.Left.Type, addr, s.mem())
case ONAME:
if n.Class == PFUNC {
// "value" of a function is the address of the function's closure
@ -2644,6 +2643,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// 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.
@ -2724,10 +2727,8 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
// that cse works on their addresses
aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
return s.newValue1A(ssa.OpAddr, t, aux, s.sp)
case PAUTO | PHEAP, PPARAM | PHEAP, PPARAMOUT | PHEAP, PPARAMREF:
return s.expr(n.Name.Heapaddr)
default:
s.Unimplementedf("variable address class %v not implemented", n.Class)
s.Unimplementedf("variable address class %v not implemented", classnames[n.Class])
return nil
}
case OINDREG:
@ -2770,17 +2771,6 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
case OCLOSUREVAR:
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
s.entryNewValue0(ssa.OpGetClosurePtr, Ptrto(Types[TUINT8])))
case OPARAM:
p := n.Left
if p.Op != ONAME || !(p.Class == PPARAM|PHEAP || p.Class == PPARAMOUT|PHEAP) {
s.Fatalf("OPARAM not of ONAME,{PPARAM,PPARAMOUT}|PHEAP, instead %s", nodedump(p, 0))
}
// Recover original offset to address passed-in param value.
original_p := *p
original_p.Xoffset = n.Xoffset
aux := &ssa.ArgSymbol{Typ: n.Type, Node: &original_p}
return s.entryNewValue1A(ssa.OpAddr, t, aux, s.sp)
case OCONVNOP:
addr := s.addr(n.Left, bounded)
return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
@ -2808,12 +2798,14 @@ func (s *state) canSSA(n *Node) bool {
if n.Addrtaken {
return false
}
if n.Class&PHEAP != 0 {
if n.isParamHeapCopy() {
return false
}
if n.Class == PAUTOHEAP {
Fatalf("canSSA of PAUTOHEAP %v", n)
}
switch n.Class {
case PEXTERN, PPARAMREF:
// TODO: maybe treat PPARAMREF with an Arg-like op to read from closure?
case PEXTERN:
return false
case PPARAMOUT:
if hasdefer {
@ -2993,6 +2985,11 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val
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))
for i, t := range results {
@ -3743,7 +3740,8 @@ func (s *state) mem() *ssa.Value {
return s.variable(&memVar, ssa.TypeMem)
}
func (s *state) linkForwardReferences() {
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,
@ -3758,13 +3756,13 @@ func (s *state) linkForwardReferences() {
for len(s.fwdRefs) > 0 {
v := s.fwdRefs[len(s.fwdRefs)-1]
s.fwdRefs = s.fwdRefs[:len(s.fwdRefs)-1]
s.resolveFwdRef(v)
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) {
func (s *state) resolveFwdRef(v *ssa.Value, dm *sparseDefState) {
b := v.Block
name := v.Aux.(*Node)
v.Aux = nil
@ -3803,6 +3801,7 @@ func (s *state) resolveFwdRef(v *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))
}

View file

@ -57,12 +57,7 @@ func TestArithmetic(t *testing.T) {
}
// TestFP tests that both backends have the same result for floating point expressions.
func TestFP(t *testing.T) {
if runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le" {
t.Skip("legacy mips64 compiler doesn't handle uint->float conversion correctly (issue 15552)")
}
runTest(t, "fp_ssa.go")
}
func TestFP(t *testing.T) { runTest(t, "fp_ssa.go") }
// TestArithmeticBoundary tests boundary results for arithmetic operations.
func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary_ssa.go") }

View file

@ -87,8 +87,39 @@ func linestr(line int32) string {
return Ctxt.Line(int(line))
}
// lasterror keeps track of the most recently issued error.
// It is used to avoid multiple error messages on the same
// line.
var lasterror struct {
syntax int32 // line of last syntax error
other int32 // line of last non-syntax error
msg string // error message of last non-syntax error
}
func yyerrorl(line int32, format string, args ...interface{}) {
adderr(line, format, args...)
msg := fmt.Sprintf(format, args...)
if strings.HasPrefix(msg, "syntax error") {
nsyntaxerrors++
// only one syntax error per line, no matter what error
if lasterror.syntax == line {
return
}
lasterror.syntax = line
} else {
// only one of multiple equal non-syntax errors per line
// (Flusherrors shows only one of them, so we filter them
// here as best as we can (they may not appear in order)
// so that we don't count them here and exit early, and
// then have nothing to show for.)
if lasterror.other == line && lasterror.msg == msg {
return
}
lasterror.other = line
lasterror.msg = msg
}
adderr(line, "%s", msg)
hcrash()
nerrors++
@ -99,32 +130,8 @@ func yyerrorl(line int32, format string, args ...interface{}) {
}
}
var yyerror_lastsyntax int32
func Yyerror(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if strings.HasPrefix(msg, "syntax error") {
nsyntaxerrors++
// only one syntax error per line
if yyerror_lastsyntax == lineno {
return
}
yyerror_lastsyntax = lineno
yyerrorl(lineno, "%s", msg)
return
}
adderr(lineno, "%s", msg)
hcrash()
nerrors++
if nsavederrors+nerrors >= 10 && Debug['e'] == 0 {
Flusherrors()
fmt.Printf("%v: too many errors\n", linestr(lineno))
errorexit()
}
yyerrorl(lineno, format, args...)
}
func Warn(fmt_ string, args ...interface{}) {
@ -1224,7 +1231,7 @@ func ullmancalc(n *Node) {
switch n.Op {
case OREGISTER, OLITERAL, ONAME:
ul = 1
if n.Class == PPARAMREF || (n.Class&PHEAP != 0) {
if n.Class == PAUTOHEAP {
ul++
}
goto out
@ -2250,6 +2257,7 @@ 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 = typecheck(x, Erv)

View file

@ -71,8 +71,45 @@ type Node struct {
Addrtaken bool // address taken, even if not moved to heap
Assigned bool // is the variable ever assigned to
Likely int8 // likeliness of if statement
Hasbreak bool // has break statement
hasVal int8 // +1 for Val, -1 for Opt, 0 for not yet set
flags uint8 // TODO: store more bool fields in this flag field
}
const (
hasBreak = 1 << iota
notLiveAtEnd
isClosureVar
)
func (n *Node) HasBreak() bool {
return n.flags&hasBreak != 0
}
func (n *Node) SetHasBreak(b bool) {
if b {
n.flags |= hasBreak
} else {
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
}
func (n *Node) setIsClosureVar(b bool) {
if b {
n.flags |= isClosureVar
} else {
n.flags &^= isClosureVar
}
}
// Val returns the Val for the node.
@ -117,15 +154,15 @@ func (n *Node) SetOpt(x interface{}) {
n.E = x
}
// Name holds Node fields used only by named nodes (ONAME, OPACK, some OLITERAL).
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, ODCLFIELD, 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
Inlvar *Node // ONAME substitute while inlining
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
Param *Param // additional fields for ONAME, ODCLFIELD
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
@ -141,16 +178,83 @@ type Name struct {
type Param struct {
Ntype *Node
// ONAME func param with PHEAP
Outerexpr *Node // expression copied into closure for variable
Stackparam *Node // OPARAM node referring to stack copy of param
// ONAME PAUTOHEAP
Stackcopy *Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only)
// ONAME PPARAM
Field *Field // TFIELD in arg struct
// ONAME closure param with PPARAMREF
Outer *Node // outer PPARAMREF in nested closure
Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
// ONAME closure linkage
// Consider:
//
// func f() {
// x := 1 // x1
// func() {
// use(x) // x2
// func() {
// use(x) // x3
// --- parser is here ---
// }()
// }()
// }
//
// There is an original declaration of x and then a chain of mentions of x
// leading into the current function. Each time x is mentioned in a new closure,
// we create a variable representing x for use in that specific closure,
// since the way you get to x is different in each closure.
//
// Let's number the specific variables as shown in the code:
// x1 is the original x, x2 is when mentioned in the closure,
// and x3 is when mentioned in the closure in the closure.
//
// We keep these linked (assume N > 1):
//
// - x1.Defn = original declaration statement for x (like most variables)
// - x1.Innermost = current innermost closure x (in this case x3), or nil for none
// - x1.isClosureVar() = false
//
// - xN.Defn = x1, N > 1
// - xN.isClosureVar() = true, N > 1
// - x2.Outer = nil
// - xN.Outer = x(N-1), N > 2
//
//
// When we look up x in the symbol table, we always get x1.
// Then we can use x1.Innermost (if not nil) to get the x
// for the innermost known closure function,
// but the first reference in a closure will find either no x1.Innermost
// or an x1.Innermost with .Funcdepth < Funcdepth.
// In that case, a new xN must be created, linked in with:
//
// xN.Defn = x1
// xN.Outer = x1.Innermost
// x1.Innermost = xN
//
// When we finish the function, we'll process its closure variables
// and find xN and pop it off the list using:
//
// x1 := xN.Defn
// x1.Innermost = xN.Outer
//
// We leave xN.Innermost set so that we can still get to the original
// variable quickly. Not shown here, but once we're
// done parsing a function and no longer need xN.Outer for the
// lexical x reference links as described above, closurebody
// recomputes xN.Outer as the semantic x reference link tree,
// even filling in x in intermediate closures that might not
// have mentioned it along the way to inner closures that did.
// See closurebody for details.
//
// During the eventual compilation, then, for closure variables we have:
//
// xN.Defn = original variable
// xN.Outer = variable captured in next outward scope
// to make closure where xN appears
//
// Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
// and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
Innermost *Node
Outer *Node
}
// Func holds Node fields used only with function-like nodes.
@ -162,9 +266,8 @@ type Func struct {
Dcl []*Node // autodcl for this func/closure
Inldcl Nodes // copy of dcl for use in inlining
Closgen int
Outerfunc *Node
Outerfunc *Node // outer function (for closure)
FieldTrack map[*Sym]struct{}
Outer *Node // outer func for closure
Ntype *Node // signature
Top int // top context (Ecall, Eproc, etc)
Closure *Node // OCLOSURE <-> ODCLFUNC
@ -266,7 +369,7 @@ const (
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)
OPARAM // variant of ONAME for on-stack copy of a parameter or return value that escapes.
_ // was OPARAM, but cannot remove without breaking binary blob in builtin.go
OLEN // len(Left)
OMAKE // make(List) (before type checking converts to one of the following)
OMAKECHAN // make(Type, Left) (type is chan)

View file

@ -0,0 +1,224 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This program generates a test to verify that the standard arithmetic
// operators properly handle constant folding. The test file should be
// generated with a known working version of go.
// launch with `go run constFoldGen.go` a file called constFold_test.go
// will be written into the grandparent directory containing the tests.
package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"log"
)
type op struct {
name, symbol string
}
type szD struct {
name string
sn string
u []uint64
i []int64
}
var szs []szD = []szD{
szD{name: "uint64", sn: "64", u: []uint64{0, 1, 4294967296, 0xffffFFFFffffFFFF}},
szD{name: "int64", sn: "64", i: []int64{-0x8000000000000000, -0x7FFFFFFFFFFFFFFF,
-4294967296, -1, 0, 1, 4294967296, 0x7FFFFFFFFFFFFFFE, 0x7FFFFFFFFFFFFFFF}},
szD{name: "uint32", sn: "32", u: []uint64{0, 1, 4294967295}},
szD{name: "int32", sn: "32", i: []int64{-0x80000000, -0x7FFFFFFF, -1, 0,
1, 0x7FFFFFFF}},
szD{name: "uint16", sn: "16", u: []uint64{0, 1, 65535}},
szD{name: "int16", sn: "16", i: []int64{-32768, -32767, -1, 0, 1, 32766, 32767}},
szD{name: "uint8", sn: "8", u: []uint64{0, 1, 255}},
szD{name: "int8", sn: "8", i: []int64{-128, -127, -1, 0, 1, 126, 127}},
}
var ops = []op{
op{"add", "+"}, op{"sub", "-"}, op{"div", "/"}, op{"mul", "*"},
op{"lsh", "<<"}, op{"rsh", ">>"}, op{"mod", "%"},
}
// compute the result of i op j, cast as type t.
func ansU(i, j uint64, t, op string) string {
var ans uint64
switch op {
case "+":
ans = i + j
case "-":
ans = i - j
case "*":
ans = i * j
case "/":
if j != 0 {
ans = i / j
}
case "%":
if j != 0 {
ans = i % j
}
case "<<":
ans = i << j
case ">>":
ans = i >> j
}
switch t {
case "uint32":
ans = uint64(uint32(ans))
case "uint16":
ans = uint64(uint16(ans))
case "uint8":
ans = uint64(uint8(ans))
}
return fmt.Sprintf("%d", ans)
}
// compute the result of i op j, cast as type t.
func ansS(i, j int64, t, op string) string {
var ans int64
switch op {
case "+":
ans = i + j
case "-":
ans = i - j
case "*":
ans = i * j
case "/":
if j != 0 {
ans = i / j
}
case "%":
if j != 0 {
ans = i % j
}
case "<<":
ans = i << uint64(j)
case ">>":
ans = i >> uint64(j)
}
switch t {
case "int32":
ans = int64(int32(ans))
case "int16":
ans = int64(int16(ans))
case "int8":
ans = int64(int8(ans))
}
return fmt.Sprintf("%d", ans)
}
func main() {
w := new(bytes.Buffer)
fmt.Fprintf(w, "package gc\n")
fmt.Fprintf(w, "import \"testing\"\n")
for _, s := range szs {
for _, o := range ops {
if o.symbol == "<<" || o.symbol == ">>" {
// shifts handled separately below, as they can have
// different types on the LHS and RHS.
continue
}
fmt.Fprintf(w, "func TestConstFold%s%s(t *testing.T) {\n", s.name, o.name)
fmt.Fprintf(w, "\tvar x, y, r %s\n", s.name)
// unsigned test cases
for _, c := range s.u {
fmt.Fprintf(w, "\tx = %d\n", c)
for _, d := range s.u {
if d == 0 && (o.symbol == "/" || o.symbol == "%") {
continue
}
fmt.Fprintf(w, "\ty = %d\n", d)
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}\n")
}
}
// signed test cases
for _, c := range s.i {
fmt.Fprintf(w, "\tx = %d\n", c)
for _, d := range s.i {
if d == 0 && (o.symbol == "/" || o.symbol == "%") {
continue
}
fmt.Fprintf(w, "\ty = %d\n", d)
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}\n")
}
}
fmt.Fprintf(w, "}\n")
}
}
// Special signed/unsigned cases for shifts
for _, ls := range szs {
for _, rs := range szs {
if rs.name[0] != 'u' {
continue
}
for _, o := range ops {
if o.symbol != "<<" && o.symbol != ">>" {
continue
}
fmt.Fprintf(w, "func TestConstFold%s%s%s(t *testing.T) {\n", ls.name, rs.name, o.name)
fmt.Fprintf(w, "\tvar x, r %s\n", ls.name)
fmt.Fprintf(w, "\tvar y %s\n", rs.name)
// unsigned LHS
for _, c := range ls.u {
fmt.Fprintf(w, "\tx = %d\n", c)
for _, d := range rs.u {
fmt.Fprintf(w, "\ty = %d\n", d)
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}\n")
}
}
// signed LHS
for _, c := range ls.i {
fmt.Fprintf(w, "\tx = %d\n", c)
for _, d := range rs.u {
fmt.Fprintf(w, "\ty = %d\n", d)
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}\n")
}
}
fmt.Fprintf(w, "}\n")
}
}
}
// gofmt result
b := w.Bytes()
src, err := format.Source(b)
if err != nil {
fmt.Printf("%s\n", b)
panic(err)
}
// write to file
err = ioutil.WriteFile("../../constFold_test.go", src, 0666)
if err != nil {
log.Fatalf("can't write output: %v\n", err)
}
}

View file

@ -223,10 +223,20 @@ type StructType struct {
// Map links such structs back to their map type.
Map *Type
Funarg bool // whether this struct represents function parameters
Funarg Funarg // type of function arguments for arg struct
Haspointers uint8 // 0 unknown, 1 no, 2 yes
}
// Fnstruct records the kind of function argument
type Funarg uint8
const (
FunargNone Funarg = iota
FunargRcvr // receiver
FunargParams // input parameters
FunargResults // output results
)
// StructType returns t's extra struct-specific fields.
func (t *Type) StructType() *StructType {
t.wantEtype(TSTRUCT)
@ -287,7 +297,7 @@ type SliceType struct {
type Field struct {
Nointerface bool
Embedded uint8 // embedded field
Funarg bool
Funarg Funarg
Broke bool // broken field definition
Isddd bool // field is ... argument
@ -786,7 +796,7 @@ func (t *Type) SetNname(n *Node) {
// IsFuncArgStruct reports whether t is a struct representing function parameters.
func (t *Type) IsFuncArgStruct() bool {
return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg
return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg != FunargNone
}
func (t *Type) Methods() *Fields {

View file

@ -796,8 +796,8 @@ OpSwitch:
var l *Node
for l = n.Left; l != r; l = l.Left {
l.Addrtaken = true
if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
l.Name.Param.Closure.Addrtaken = true
if l.isClosureVar() {
l.Name.Defn.Addrtaken = true
}
}
@ -805,8 +805,8 @@ OpSwitch:
Fatalf("found non-orig name node %v", l)
}
l.Addrtaken = true
if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
l.Name.Param.Closure.Addrtaken = true
if l.isClosureVar() {
l.Name.Defn.Addrtaken = true
}
n.Left = defaultlit(n.Left, nil)
l = n.Left
@ -3099,7 +3099,7 @@ func islvalue(n *Node) bool {
return false
}
fallthrough
case OIND, ODOTPTR, OCLOSUREVAR, OPARAM:
case OIND, ODOTPTR, OCLOSUREVAR:
return true
case ODOT:
@ -3128,14 +3128,14 @@ func checkassign(stmt *Node, n *Node) {
var l *Node
for l = n; l != r; l = l.Left {
l.Assigned = true
if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
l.Name.Param.Closure.Assigned = true
if l.isClosureVar() {
l.Name.Defn.Assigned = true
}
}
l.Assigned = true
if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
l.Name.Param.Closure.Assigned = true
if l.isClosureVar() {
l.Name.Defn.Assigned = true
}
}
@ -3153,7 +3153,7 @@ func checkassign(stmt *Node, n *Node) {
}
if n.Op == ODOT && n.Left.Op == OINDEXMAP {
Yyerror("cannot directly assign to struct field %v in map", n)
Yyerror("cannot assign to struct field %v in map", n)
return
}
@ -3786,12 +3786,12 @@ func markbreak(n *Node, implicit *Node) {
case OBREAK:
if n.Left == nil {
if implicit != nil {
implicit.Hasbreak = true
implicit.SetHasBreak(true)
}
} else {
lab := n.Left.Sym.Label
if lab != nil {
lab.Def.Hasbreak = true
lab.Def.SetHasBreak(true)
}
}
@ -3867,7 +3867,7 @@ func (n *Node) isterminating() bool {
if n.Left != nil {
return false
}
if n.Hasbreak {
if n.HasBreak() {
return false
}
return true
@ -3876,7 +3876,7 @@ func (n *Node) isterminating() bool {
return n.Nbody.isterminating() && n.Rlist.isterminating()
case OSWITCH, OTYPESW, OSELECT:
if n.Hasbreak {
if n.HasBreak() {
return false
}
def := 0

View file

@ -362,16 +362,16 @@ func lexinit1() {
// t = interface { Error() string }
rcvr := typ(TSTRUCT)
rcvr.StructType().Funarg = true
rcvr.StructType().Funarg = FunargRcvr
field := newField()
field.Type = Ptrto(typ(TSTRUCT))
rcvr.SetFields([]*Field{field})
in := typ(TSTRUCT)
in.StructType().Funarg = true
in.StructType().Funarg = FunargParams
out := typ(TSTRUCT)
out.StructType().Funarg = true
out.StructType().Funarg = FunargResults
field = newField()
field.Type = Types[TSTRING]
out.SetFields([]*Field{field})

View file

@ -27,9 +27,8 @@ func walk(fn *Node) {
lno := lineno
// Final typecheck for any unused variables.
// It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
for i, ln := range fn.Func.Dcl {
if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO {
if ln.Op == ONAME && (ln.Class == PAUTO || ln.Class == PAUTOHEAP) {
ln = typecheck(ln, Erv|Easgn)
fn.Func.Dcl[i] = ln
}
@ -37,13 +36,13 @@ func walk(fn *Node) {
// Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
for _, ln := range fn.Func.Dcl {
if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used {
if ln.Op == ONAME && (ln.Class == PAUTO || ln.Class == PAUTOHEAP) && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used {
ln.Name.Defn.Left.Used = true
}
}
for _, ln := range fn.Func.Dcl {
if ln.Op != ONAME || ln.Class&^PHEAP != PAUTO || ln.Sym.Name[0] == '&' || ln.Used {
if ln.Op != ONAME || (ln.Class != PAUTO && ln.Class != PAUTOHEAP) || ln.Sym.Name[0] == '&' || ln.Used {
continue
}
if defn := ln.Name.Defn; defn != nil && defn.Op == OTYPESW {
@ -97,13 +96,13 @@ func samelist(a, b []*Node) bool {
func paramoutheap(fn *Node) bool {
for _, ln := range fn.Func.Dcl {
switch ln.Class {
case PPARAMOUT,
PPARAMOUT | PHEAP:
return ln.Addrtaken
case PPARAMOUT:
if ln.isParamStackCopy() || ln.Addrtaken {
return true
}
case PAUTO:
// stop early - parameters are over
case PAUTO,
PAUTO | PHEAP:
return false
}
}
@ -212,7 +211,6 @@ func walkstmt(n *Node) *Node {
n = addinit(n, init.Slice())
case OBREAK,
ODCL,
OCONTINUE,
OFALL,
OGOTO,
@ -224,6 +222,21 @@ func walkstmt(n *Node) *Node {
OVARLIVE:
break
case ODCL:
v := n.Left
if v.Class == PAUTOHEAP {
if compiling_runtime {
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.Colas = true
nn = typecheck(nn, Etop)
return walkstmt(nn)
}
case OBLOCK:
walkstmtlist(n.List.Slice())
@ -295,11 +308,14 @@ func walkstmt(n *Node) *Node {
var cl Class
for _, ln := range Curfn.Func.Dcl {
cl = ln.Class &^ PHEAP
if cl == PAUTO {
cl = ln.Class
if cl == PAUTO || cl == PAUTOHEAP {
break
}
if cl == PPARAMOUT {
if ln.isParamStackCopy() {
ln = walkexpr(typecheck(Nod(OIND, ln.Name.Heapaddr, nil), Erv), nil)
}
rl = append(rl, ln)
}
}
@ -487,6 +503,12 @@ func walkexpr(n *Node, init *Nodes) *Node {
Fatalf("missed typecheck: %v\n", Nconv(n, FmtSign))
}
if n.Op == ONAME && n.Class == PAUTOHEAP {
nn := Nod(OIND, n.Name.Heapaddr, nil)
nn = typecheck(nn, Erv)
return walkexpr(nn, init)
}
opswitch:
switch n.Op {
default:
@ -497,7 +519,6 @@ opswitch:
ONONAME,
OINDREG,
OEMPTY,
OPARAM,
OGETG:
case ONOT,
@ -626,9 +647,7 @@ opswitch:
n.Addable = true
case ONAME:
if n.Class&PHEAP == 0 && n.Class != PPARAMREF {
n.Addable = true
}
case OCALLINTER:
usemethod(n)
@ -1640,7 +1659,7 @@ func ascompatee(op Op, nl, nr []*Node, init *Nodes) []*Node {
break
}
// Do not generate 'x = x' during return. See issue 4014.
if op == ORETURN && nl[i] == nr[i] {
if op == ORETURN && samesafeexpr(nl[i], nr[i]) {
continue
}
nn = append(nn, ascompatee1(op, nl[i], nr[i], init))
@ -2515,7 +2534,7 @@ func vmatch1(l *Node, r *Node) bool {
switch l.Op {
case ONAME:
switch l.Class {
case PPARAM, PPARAMREF, PAUTO:
case PPARAM, PAUTO:
break
// assignment to non-stack variable
@ -2550,41 +2569,31 @@ func vmatch1(l *Node, r *Node) bool {
// and to copy non-result prameters' values from the stack.
// If out is true, then code is also produced to zero-initialize their
// stack memory addresses.
func paramstoheap(params *Type, out bool) []*Node {
func paramstoheap(params *Type) []*Node {
var nn []*Node
for _, t := range params.Fields().Slice() {
// For precise stacks, the garbage collector assumes results
// are always live, so zero them always.
if params.StructType().Funarg == FunargResults {
// 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))
}
v := t.Nname
if v != nil && v.Sym != nil && strings.HasPrefix(v.Sym.Name, "~r") { // unnamed result
v = nil
}
// For precise stacks, the garbage collector assumes results
// are always live, so zero them always.
if out {
// 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))
}
if v == nil || v.Class&PHEAP == 0 {
if v == nil {
continue
}
// generate allocation & copying code
if compiling_runtime {
Yyerror("%v escapes to heap, not allowed in runtime.", v)
if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil {
nn = append(nn, walkstmt(Nod(ODCL, v, nil)))
if stackcopy.Class == PPARAM {
nn = append(nn, walkstmt(typecheck(Nod(OAS, v, stackcopy), Etop)))
}
if prealloc[v] == nil {
prealloc[v] = callnew(v.Type)
}
nn = append(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v]))
if v.Class&^PHEAP != PPARAMOUT {
as := Nod(OAS, v, v.Name.Param.Stackparam)
v.Name.Param.Stackparam.Typecheck = 1
as = typecheck(as, Etop)
as = applywritebarrier(as)
nn = append(nn, as)
}
}
@ -2597,10 +2606,12 @@ func returnsfromheap(params *Type) []*Node {
var nn []*Node
for _, t := range params.Fields().Slice() {
v := t.Nname
if v == nil || v.Class != PHEAP|PPARAMOUT {
if v == nil {
continue
}
nn = append(nn, Nod(OAS, v.Name.Param.Stackparam, v))
if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil && stackcopy.Class == PPARAMOUT {
nn = append(nn, walkstmt(typecheck(Nod(OAS, stackcopy, v), Etop)))
}
}
return nn
@ -2612,9 +2623,9 @@ func returnsfromheap(params *Type) []*Node {
func heapmoves() {
lno := lineno
lineno = Curfn.Lineno
nn := paramstoheap(Curfn.Type.Recvs(), false)
nn = append(nn, paramstoheap(Curfn.Type.Params(), false)...)
nn = append(nn, paramstoheap(Curfn.Type.Results(), true)...)
nn := paramstoheap(Curfn.Type.Recvs())
nn = append(nn, paramstoheap(Curfn.Type.Params())...)
nn = append(nn, paramstoheap(Curfn.Type.Results())...)
Curfn.Func.Enter.Append(nn...)
lineno = Curfn.Func.Endlineno
Curfn.Func.Exit.Append(returnsfromheap(Curfn.Type.Results())...)

View file

@ -466,7 +466,7 @@ func gmove(f *gc.Node, t *gc.Node) {
//return;
// algorithm is:
// if small enough, use native int64 -> float64 conversion.
// otherwise, halve (rounding to odd?), convert, and double.
// otherwise, halve (x -> (x>>1)|(x&1)), convert, and double.
/*
* integer to float
*/
@ -496,9 +496,16 @@ func gmove(f *gc.Node, t *gc.Node) {
gmove(&bigi, &rtmp)
gins(mips.AAND, &r1, &rtmp)
p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0)
p2 := gins(mips.ASRLV, nil, &r1)
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)
}

View file

@ -316,7 +316,7 @@ func checkFunc(f *Func) {
}
// domCheck reports whether x dominates y (including x==y).
func domCheck(f *Func, sdom sparseTree, x, y *Block) bool {
func domCheck(f *Func, sdom SparseTree, x, y *Block) bool {
if !sdom.isAncestorEq(f.Entry, y) {
// unreachable - ignore
return true

View file

@ -86,14 +86,14 @@ func Compile(f *Func) {
// Surround timing information w/ enough context to allow comparisons.
time := tEnd.Sub(tStart).Nanoseconds()
if p.time {
f.logStat("TIME(ns)", time)
f.LogStat("TIME(ns)", time)
}
if p.mem {
var mEnd runtime.MemStats
runtime.ReadMemStats(&mEnd)
nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
nAllocs := mEnd.Mallocs - mStart.Mallocs
f.logStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
}
}
if checkEnabled {
@ -124,6 +124,10 @@ var checkEnabled = false
var IntrinsicsDebug int
var IntrinsicsDisable bool
var BuildDebug int
var BuildTest int
var BuildStats int
// PhaseOption sets the specified flag in the specified ssa phase,
// returning empty string if this was successful or a string explaining
// the error if it was not.
@ -174,6 +178,19 @@ func PhaseOption(phase, flag string, val int) string {
}
return ""
}
if phase == "build" {
switch flag {
case "debug":
BuildDebug = val
case "test":
BuildTest = val
case "stats":
BuildStats = val
default:
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
}
return ""
}
underphase := strings.Replace(phase, "_", " ", -1)
var re *regexp.Regexp

View file

@ -9,6 +9,7 @@ import (
"crypto/sha1"
"fmt"
"os"
"strconv"
"strings"
)
@ -25,6 +26,7 @@ type Config struct {
ctxt *obj.Link // Generic arch information
optimize bool // Do optimization
noDuffDevice bool // Don't use Duff's device
sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
curFunc *Func
// TODO: more stuff. Compiler flags of interest, ...
@ -162,10 +164,27 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config
c.logfiles = make(map[string]*os.File)
// cutoff 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 == -1 implies none sparse.
// Good cutoff values seem to be O(million) depending on constant factor cost of sparse.
// TODO: get this from a flag, not an environment variable
c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash
ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF")
if ev != "" {
v, err := strconv.ParseInt(ev, 10, 64)
if err != nil {
fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev)
}
c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse
}
return c
}
func (c *Config) Frontend() Frontend { return c.fe }
func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
// NewFunc returns a new, empty function object.
// Caller must call f.Free() before calling NewFunc again.
@ -262,3 +281,7 @@ func (c *Config) DebugHashMatch(evname, name string) bool {
}
return false
}
func (c *Config) DebugNameMatch(evname, name string) bool {
return os.Getenv(evname) == name
}

View file

@ -137,10 +137,9 @@ func cse(f *Func) {
// if v and w are in the same equivalence class and v dominates w.
rewrite := make([]*Value, f.NumValues())
for _, e := range partition {
sort.Sort(sortbyentry{e, f.sdom})
sort.Sort(partitionByDom{e, f.sdom})
for i := 0; i < len(e)-1; i++ {
// e is sorted by entry value so maximal dominant element should be
// found first in the slice
// e is sorted by domorder, so a maximal dominant element is first in the slice
v := e[i]
if v == nil {
continue
@ -157,9 +156,7 @@ func cse(f *Func) {
rewrite[w.ID] = v
e[j] = nil
} else {
// since the blocks are assorted in ascending order by entry number
// once we know that we don't dominate a block we can't dominate any
// 'later' block
// e is sorted by domorder, so v.Block doesn't dominate any subsequent blocks in e
break
}
}
@ -190,7 +187,7 @@ func cse(f *Func) {
}
}
if f.pass.stats > 0 {
f.logStat("CSE REWRITES", rewrites)
f.LogStat("CSE REWRITES", rewrites)
}
}
@ -311,15 +308,15 @@ func (sv sortvalues) Less(i, j int) bool {
return v.ID < w.ID
}
type sortbyentry struct {
type partitionByDom struct {
a []*Value // array of values
sdom sparseTree
sdom SparseTree
}
func (sv sortbyentry) Len() int { return len(sv.a) }
func (sv sortbyentry) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
func (sv sortbyentry) Less(i, j int) bool {
func (sv partitionByDom) Len() int { return len(sv.a) }
func (sv partitionByDom) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
func (sv partitionByDom) Less(i, j int) bool {
v := sv.a[i]
w := sv.a[j]
return sv.sdom.maxdomorder(v.Block) < sv.sdom.maxdomorder(w.Block)
return sv.sdom.domorder(v.Block) < sv.sdom.domorder(w.Block)
}

View file

@ -20,9 +20,9 @@ const (
// postorder computes a postorder traversal ordering for the
// basic blocks in f. Unreachable blocks will not appear.
func postorder(f *Func) []*Block {
return postorderWithNumbering(f, []int{})
return postorderWithNumbering(f, []int32{})
}
func postorderWithNumbering(f *Func, ponums []int) []*Block {
func postorderWithNumbering(f *Func, ponums []int32) []*Block {
mark := make([]markKind, f.NumBlocks())
// result ordering
@ -40,7 +40,7 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block {
s = s[:len(s)-1]
mark[b.ID] = done
if len(ponums) > 0 {
ponums[b.ID] = len(order)
ponums[b.ID] = int32(len(order))
}
order = append(order, b)
case notExplored:

View file

@ -37,7 +37,7 @@ type Func struct {
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
sdom SparseTree // precomputed dominator tree
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
}
@ -104,12 +104,16 @@ func (f *Func) newValue(op Op, t Type, b *Block, line int32) *Value {
// context to allow item-by-item comparisons across runs.
// For example:
// awk 'BEGIN {FS="\t"} $3~/TIME/{sum+=$4} END{print "t(ns)=",sum}' t.log
func (f *Func) logStat(key string, args ...interface{}) {
func (f *Func) LogStat(key string, args ...interface{}) {
value := ""
for _, a := range args {
value += fmt.Sprintf("\t%v", a)
}
f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", f.pass.name, key, value, f.Name)
n := "missing_pass"
if f.pass != nil {
n = f.pass.name
}
f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name)
}
// freeValue frees a value. It must no longer be referenced.

View file

@ -383,8 +383,8 @@ var genericOps = []opData{
// Strings
{name: "StringMake", argLength: 2}, // arg0=ptr, arg1=len
{name: "StringPtr", argLength: 1}, // ptr(arg0)
{name: "StringLen", argLength: 1}, // len(arg0)
{name: "StringPtr", argLength: 1, typ: "BytePtr"}, // ptr(arg0)
{name: "StringLen", argLength: 1, typ: "Int"}, // len(arg0)
// Interfaces
{name: "IMake", argLength: 2}, // arg0=itab, arg1=data
@ -407,7 +407,7 @@ var genericOps = []opData{
{name: "LoadReg", argLength: 1},
// Used during ssa construction. Like Copy, but the arg has not been specified yet.
{name: "FwdRef"},
{name: "FwdRef", aux: "Sym"},
// Unknown value. Used for Values whose values don't matter because they are dead code.
{name: "Unknown"},
@ -415,6 +415,7 @@ var genericOps = []opData{
{name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem"}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem
{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
}
// kind control successors implicit exit

View file

@ -150,9 +150,6 @@ func genRules(arch arch) {
fmt.Fprintln(w, "// generated with: cd gen; go run *.go")
fmt.Fprintln(w)
fmt.Fprintln(w, "package ssa")
if *genLog {
fmt.Fprintln(w, "import \"fmt\"")
}
fmt.Fprintln(w, "import \"math\"")
fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used")
@ -196,7 +193,7 @@ func genRules(arch arch) {
genResult(w, arch, result, rule.loc)
if *genLog {
fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc)
fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc)
}
fmt.Fprintf(w, "return true\n")
@ -300,7 +297,7 @@ func genRules(arch arch) {
}
if *genLog {
fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc)
fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc)
}
fmt.Fprintf(w, "return true\n")

View file

@ -32,7 +32,7 @@ type loop struct {
}
// outerinner records that outer contains inner
func (sdom sparseTree) outerinner(outer, inner *loop) {
func (sdom SparseTree) outerinner(outer, inner *loop) {
oldouter := inner.outer
if oldouter == nil || sdom.isAncestorEq(oldouter.header, outer.header) {
inner.outer = outer
@ -59,7 +59,7 @@ type loopnest struct {
f *Func
b2l []*loop
po []*Block
sdom sparseTree
sdom SparseTree
loops []*loop
// Record which of the lazily initialized fields have actually been initialized.
@ -238,7 +238,7 @@ func (l *loop) LongString() string {
// containing block b; the header must dominate b. loop itself
// is assumed to not be that loop. For acceptable performance,
// we're relying on loop nests to not be terribly deep.
func (l *loop) nearestOuterLoop(sdom sparseTree, b *Block) *loop {
func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop {
var o *loop
for o = l.outer; o != nil && !sdom.isAncestorEq(o.header, b); o = o.outer {
}
@ -335,7 +335,7 @@ func loopnestfor(f *Func) *loopnest {
inner++
}
f.logStat("loopstats:",
f.LogStat("loopstats:",
l.depth, "depth", x, "exits",
inner, "is_inner", cf, "is_callfree", l.nBlocks, "n_blocks")
}

View file

@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered
}
switch v.Op {
case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive:
case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive:
continue // ok not to lower
}
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()

View file

@ -674,6 +674,7 @@ const (
OpVarDef
OpVarKill
OpVarLive
OpKeepAlive
)
var opcodeTable = [...]opInfo{
@ -6167,6 +6168,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "FwdRef",
auxType: auxSym,
argLen: 0,
generic: true,
},
@ -6193,6 +6195,11 @@ var opcodeTable = [...]opInfo{
argLen: 1,
generic: true,
},
{
name: "KeepAlive",
argLen: 2,
generic: true,
},
}
func (o Op) Asm() obj.As { return opcodeTable[o].asm }

View file

@ -35,7 +35,7 @@ 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 +51,7 @@ 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++ {

View file

@ -515,7 +515,7 @@ func prove(f *Func) {
// getBranch returns the range restrictions added by p
// when reaching b. p is the immediate dominator of b.
func getBranch(sdom sparseTree, p *Block, b *Block) branch {
func getBranch(sdom SparseTree, p *Block, b *Block) branch {
if p == nil || p.Kind != BlockIf {
return unknown
}

View file

@ -0,0 +1,429 @@
// Copyright 2016 The Go Authors. 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"
const (
rankLeaf rbrank = 1
rankZero rbrank = 0
)
type rbrank int8
// RBTint32 is a red-black tree with data stored at internal nodes,
// following Tarjan, Data Structures and Network Algorithms,
// pp 48-52, using explicit rank instead of red and black.
// Deletion is not yet implemented because it is not yet needed.
// Extra operations glb, lub, glbEq, lubEq are provided for
// use in sparse lookup algorithms.
type RBTint32 struct {
root *node32
// An extra-clever implementation will have special cases
// for small sets, but we are not extra-clever today.
}
func (t *RBTint32) String() string {
if t.root == nil {
return "[]"
}
return "[" + t.root.String() + "]"
}
func (t *node32) String() string {
s := ""
if t.left != nil {
s = t.left.String() + " "
}
s = s + fmt.Sprintf("k=%d,d=%v", t.key, t.data)
if t.right != nil {
s = s + " " + t.right.String()
}
return s
}
type node32 struct {
// Standard conventions hold for left = smaller, right = larger
left, right, parent *node32
data interface{}
key int32
rank rbrank // From Tarjan pp 48-49:
// If x is a node with a parent, then x.rank <= x.parent.rank <= x.rank+1.
// If x is a node with a grandparent, then x.rank < x.parent.parent.rank.
// If x is an "external [null] node", then x.rank = 0 && x.parent.rank = 1.
// Any node with one or more null children should have rank = 1.
}
// makeNode returns a new leaf node with the given key and nil data.
func (t *RBTint32) makeNode(key int32) *node32 {
return &node32{key: key, rank: rankLeaf}
}
// IsEmpty reports whether t is empty.
func (t *RBTint32) IsEmpty() bool {
return t.root == nil
}
// IsSingle reports whether t is a singleton (leaf).
func (t *RBTint32) IsSingle() bool {
return t.root != nil && t.root.isLeaf()
}
// VisitInOrder applies f to the key and data pairs in t,
// with keys ordered from smallest to largest.
func (t *RBTint32) VisitInOrder(f func(int32, interface{})) {
if t.root == nil {
return
}
t.root.visitInOrder(f)
}
func (n *node32) Data() interface{} {
if n == nil {
return nil
}
return n.data
}
func (n *node32) keyAndData() (k int32, d interface{}) {
if n == nil {
k = 0
d = nil
} else {
k = n.key
d = n.data
}
return
}
func (n *node32) Rank() rbrank {
if n == nil {
return 0
}
return n.rank
}
// Find returns the data associated with key in the tree, or
// nil if key is not in the tree.
func (t *RBTint32) Find(key int32) interface{} {
return t.root.find(key).Data()
}
// Insert adds key to the tree and associates key with data.
// If key was already in the tree, it updates the associated data.
// Insert returns the previous data associated with key,
// or nil if key was not present.
// Insert panics if data is nil.
func (t *RBTint32) Insert(key int32, data interface{}) interface{} {
if data == nil {
panic("Cannot insert nil data into tree")
}
n := t.root
var newroot *node32
if n == nil {
n = t.makeNode(key)
newroot = n
} else {
newroot, n = n.insert(key, t)
}
r := n.data
n.data = data
t.root = newroot
return r
}
// Min returns the minimum element of t and its associated data.
// If t is empty, then (0, nil) is returned.
func (t *RBTint32) Min() (k int32, d interface{}) {
return t.root.min().keyAndData()
}
// Max returns the maximum element of t and its associated data.
// If t is empty, then (0, nil) is returned.
func (t *RBTint32) Max() (k int32, d interface{}) {
return t.root.max().keyAndData()
}
// Glb returns the greatest-lower-bound-exclusive of x and its associated
// data. If x has no glb in the tree, then (0, nil) is returned.
func (t *RBTint32) Glb(x int32) (k int32, d interface{}) {
return t.root.glb(x, false).keyAndData()
}
// GlbEq returns the greatest-lower-bound-inclusive of x and its associated
// data. If x has no glbEQ in the tree, then (0, nil) is returned.
func (t *RBTint32) GlbEq(x int32) (k int32, d interface{}) {
return t.root.glb(x, true).keyAndData()
}
// Lub returns the least-upper-bound-exclusive of x and its associated
// data. If x has no lub in the tree, then (0, nil) is returned.
func (t *RBTint32) Lub(x int32) (k int32, d interface{}) {
return t.root.lub(x, false).keyAndData()
}
// LubEq returns the least-upper-bound-inclusive of x and its associated
// data. If x has no lubEq in the tree, then (0, nil) is returned.
func (t *RBTint32) LubEq(x int32) (k int32, d interface{}) {
return t.root.lub(x, true).keyAndData()
}
func (t *node32) isLeaf() bool {
return t.left == nil && t.right == nil
}
func (t *node32) visitInOrder(f func(int32, interface{})) {
if t.left != nil {
t.left.visitInOrder(f)
}
f(t.key, t.data)
if t.right != nil {
t.right.visitInOrder(f)
}
}
func (t *node32) maxChildRank() rbrank {
if t.left == nil {
if t.right == nil {
return rankZero
}
return t.right.rank
}
if t.right == nil {
return t.left.rank
}
if t.right.rank > t.left.rank {
return t.right.rank
}
return t.left.rank
}
func (t *node32) minChildRank() rbrank {
if t.left == nil || t.right == nil {
return rankZero
}
if t.right.rank < t.left.rank {
return t.right.rank
}
return t.left.rank
}
func (t *node32) find(key int32) *node32 {
for t != nil {
if key < t.key {
t = t.left
} else if key > t.key {
t = t.right
} else {
return t
}
}
return nil
}
func (t *node32) min() *node32 {
if t == nil {
return t
}
for t.left != nil {
t = t.left
}
return t
}
func (t *node32) max() *node32 {
if t == nil {
return t
}
for t.right != nil {
t = t.right
}
return t
}
func (t *node32) glb(key int32, allow_eq bool) *node32 {
var best *node32 = nil
for t != nil {
if key <= t.key {
if key == t.key && allow_eq {
return t
}
// t is too big, glb is to left.
t = t.left
} else {
// t is a lower bound, record it and seek a better one.
best = t
t = t.right
}
}
return best
}
func (t *node32) lub(key int32, allow_eq bool) *node32 {
var best *node32 = nil
for t != nil {
if key >= t.key {
if key == t.key && allow_eq {
return t
}
// t is too small, lub is to right.
t = t.right
} else {
// t is a upper bound, record it and seek a better one.
best = t
t = t.left
}
}
return best
}
func (t *node32) insert(x int32, w *RBTint32) (newroot, newnode *node32) {
// defaults
newroot = t
newnode = t
if x == t.key {
return
}
if x < t.key {
if t.left == nil {
n := w.makeNode(x)
n.parent = t
t.left = n
newnode = n
return
}
var new_l *node32
new_l, newnode = t.left.insert(x, w)
t.left = new_l
new_l.parent = t
newrank := 1 + new_l.maxChildRank()
if newrank > t.rank {
if newrank > 1+t.right.Rank() { // rotations required
if new_l.left.Rank() < new_l.right.Rank() {
// double rotation
t.left = new_l.rightToRoot()
}
newroot = t.leftToRoot()
return
} else {
t.rank = newrank
}
}
} else { // x > t.key
if t.right == nil {
n := w.makeNode(x)
n.parent = t
t.right = n
newnode = n
return
}
var new_r *node32
new_r, newnode = t.right.insert(x, w)
t.right = new_r
new_r.parent = t
newrank := 1 + new_r.maxChildRank()
if newrank > t.rank {
if newrank > 1+t.left.Rank() { // rotations required
if new_r.right.Rank() < new_r.left.Rank() {
// double rotation
t.right = new_r.leftToRoot()
}
newroot = t.rightToRoot()
return
} else {
t.rank = newrank
}
}
}
return
}
func (t *node32) rightToRoot() *node32 {
// this
// left right
// rl rr
//
// becomes
//
// right
// this rr
// left rl
//
right := t.right
rl := right.left
right.parent = t.parent
right.left = t
t.parent = right
// parent's child ptr fixed in caller
t.right = rl
if rl != nil {
rl.parent = t
}
return right
}
func (t *node32) leftToRoot() *node32 {
// this
// left right
// ll lr
//
// becomes
//
// left
// ll this
// lr right
//
left := t.left
lr := left.right
left.parent = t.parent
left.right = t
t.parent = left
// parent's child ptr fixed in caller
t.left = lr
if lr != nil {
lr.parent = t
}
return left
}
// next returns the successor of t in a left-to-right
// walk of the tree in which t is embedded.
func (t *node32) next() *node32 {
// If there is a right child, it is to the right
r := t.right
if r != nil {
return r.min()
}
// if t is p.left, then p, else repeat.
p := t.parent
for p != nil {
if p.left == t {
return p
}
t = p
p = t.parent
}
return nil
}
// prev returns the predecessor of t in a left-to-right
// walk of the tree in which t is embedded.
func (t *node32) prev() *node32 {
// If there is a left child, it is to the left
l := t.left
if l != nil {
return l.max()
}
// if t is p.right, then p, else repeat.
p := t.parent
for p != nil {
if p.right == t {
return p
}
t = p
p = t.parent
}
return nil
}

View file

@ -0,0 +1,276 @@
// Copyright 2016 The Go Authors. 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"
"testing"
)
type sstring string
func (s sstring) String() string {
return string(s)
}
// wellFormed ensures that a red-black tree meets
// all of its invariants and returns a string identifying
// the first problem encountered. If there is no problem
// then the returned string is empty. The size is also
// returned to allow comparison of calculated tree size
// with expected.
func (t *RBTint32) wellFormed() (s string, i int) {
if t.root == nil {
s = ""
i = 0
return
}
return t.root.wellFormedSubtree(nil, -0x80000000, 0x7fffffff)
}
// wellFormedSubtree ensures that a red-black subtree meets
// all of its invariants and returns a string identifying
// the first problem encountered. If there is no problem
// then the returned string is empty. The size is also
// returned to allow comparison of calculated tree size
// with expected.
func (t *node32) wellFormedSubtree(parent *node32, min, max int32) (s string, i int) {
i = -1 // initialize to a failing value
s = "" // s is the reason for failure; empty means okay.
if t.parent != parent {
s = "t.parent != parent"
return
}
if min >= t.key {
s = "min >= t.key"
return
}
if max <= t.key {
s = "max <= t.key"
return
}
l := t.left
r := t.right
if l == nil && r == nil {
if t.rank != rankLeaf {
s = "leaf rank wrong"
return
}
}
if l != nil {
if t.rank < l.rank {
s = "t.rank < l.rank"
} else if t.rank > 1+l.rank {
s = "t.rank > 1+l.rank"
} else if t.rank <= l.maxChildRank() {
s = "t.rank <= l.maxChildRank()"
} else if t.key <= l.key {
s = "t.key <= l.key"
}
if s != "" {
return
}
} else {
if t.rank != 1 {
s = "t w/ left nil has rank != 1"
return
}
}
if r != nil {
if t.rank < r.rank {
s = "t.rank < r.rank"
} else if t.rank > 1+r.rank {
s = "t.rank > 1+r.rank"
} else if t.rank <= r.maxChildRank() {
s = "t.rank <= r.maxChildRank()"
} else if t.key >= r.key {
s = "t.key >= r.key"
}
if s != "" {
return
}
} else {
if t.rank != 1 {
s = "t w/ right nil has rank != 1"
return
}
}
ii := 1
if l != nil {
res, il := l.wellFormedSubtree(t, min, t.key)
if res != "" {
s = "L." + res
return
}
ii += il
}
if r != nil {
res, ir := r.wellFormedSubtree(t, t.key, max)
if res != "" {
s = "R." + res
return
}
ii += ir
}
i = ii
return
}
func (t *RBTint32) DebugString() string {
if t.root == nil {
return ""
}
return t.root.DebugString()
}
// DebugString prints the tree with nested information
// to allow an eyeball check on the tree balance.
func (t *node32) DebugString() string {
s := ""
if t.left != nil {
s = s + "["
s = s + t.left.DebugString()
s = s + "]"
}
s = s + fmt.Sprintf("%v=%v:%d", t.key, t.data, t.rank)
if t.right != nil {
s = s + "["
s = s + t.right.DebugString()
s = s + "]"
}
return s
}
func allRBT32Ops(te *testing.T, x []int32) {
t := &RBTint32{}
for i, d := range x {
x[i] = d + d // Double everything for glb/lub testing
}
// fmt.Printf("Inserting double of %v", x)
k := 0
min := int32(0x7fffffff)
max := int32(-0x80000000)
for _, d := range x {
if d < min {
min = d
}
if d > max {
max = d
}
t.Insert(d, sstring(fmt.Sprintf("%v", d)))
k++
s, i := t.wellFormed()
if i != k {
te.Errorf("Wrong tree size %v, expected %v for %v", i, k, t.DebugString())
}
if s != "" {
te.Errorf("Tree consistency problem at %v", s)
return
} else {
// fmt.Printf("%s", t.DebugString())
}
}
oops := false
for _, d := range x {
s := fmt.Sprintf("%v", d)
f := t.Find(d)
// data
if s != fmt.Sprintf("%v", f) {
te.Errorf("s(%v) != f(%v)", s, f)
oops = true
}
}
if !oops {
for _, d := range x {
s := fmt.Sprintf("%v", d)
kg, g := t.Glb(d + 1)
kge, ge := t.GlbEq(d)
kl, l := t.Lub(d - 1)
kle, le := t.LubEq(d)
// keys
if d != kg {
te.Errorf("d(%v) != kg(%v)", d, kg)
}
if d != kl {
te.Errorf("d(%v) != kl(%v)", d, kl)
}
if d != kge {
te.Errorf("d(%v) != kge(%v)", d, kge)
}
if d != kle {
te.Errorf("d(%v) != kle(%v)", d, kle)
}
// data
if s != fmt.Sprintf("%v", g) {
te.Errorf("s(%v) != g(%v)", s, g)
}
if s != fmt.Sprintf("%v", l) {
te.Errorf("s(%v) != l(%v)", s, l)
}
if s != fmt.Sprintf("%v", ge) {
te.Errorf("s(%v) != ge(%v)", s, ge)
}
if s != fmt.Sprintf("%v", le) {
te.Errorf("s(%v) != le(%v)", s, le)
}
}
for _, d := range x {
s := fmt.Sprintf("%v", d)
kge, ge := t.GlbEq(d + 1)
kle, le := t.LubEq(d - 1)
if d != kge {
te.Errorf("d(%v) != kge(%v)", d, kge)
}
if d != kle {
te.Errorf("d(%v) != kle(%v)", d, kle)
}
if s != fmt.Sprintf("%v", ge) {
te.Errorf("s(%v) != ge(%v)", s, ge)
}
if s != fmt.Sprintf("%v", le) {
te.Errorf("s(%v) != le(%v)", s, le)
}
}
kg, g := t.Glb(min)
kge, ge := t.GlbEq(min - 1)
kl, l := t.Lub(max)
kle, le := t.LubEq(max + 1)
fmin := t.Find(min - 1)
fmax := t.Find(min + 11)
if kg != 0 || kge != 0 || kl != 0 || kle != 0 {
te.Errorf("Got non-zero-key for missing query")
}
if g != nil || ge != nil || l != nil || le != nil || fmin != nil || fmax != nil {
te.Errorf("Got non-error-data for missing query")
}
}
}
func TestAllRBTreeOps(t *testing.T) {
allRBT32Ops(t, []int32{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})
allRBT32Ops(t, []int32{22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 3, 2, 1, 25, 24, 23, 12, 11, 10, 9, 8, 7, 6, 5, 4})
allRBT32Ops(t, []int32{25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1})
allRBT32Ops(t, []int32{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24})
allRBT32Ops(t, []int32{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2})
allRBT32Ops(t, []int32{24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25})
}

View file

@ -106,7 +106,6 @@
package ssa
import (
"cmd/internal/obj"
"fmt"
"unsafe"
)
@ -456,7 +455,7 @@ func (s *regAllocState) init(f *Func) {
s.allocatable = regMask(1)<<s.numRegs - 1
s.allocatable &^= 1 << s.SPReg
s.allocatable &^= 1 << s.SBReg
if obj.Framepointer_enabled != 0 {
if s.f.Config.ctxt.Framepointer_enabled {
s.allocatable &^= 1 << 5 // BP
}
if s.f.Config.ctxt.Flag_dynlink {
@ -941,11 +940,29 @@ func (s *regAllocState) regalloc(f *Func) {
s.advanceUses(v)
continue
}
if v.Op == OpKeepAlive {
// Make sure the argument to v is still live here.
s.advanceUses(v)
vi := &s.values[v.Args[0].ID]
if vi.spillUsed {
// Use the spill location.
v.SetArg(0, vi.spill)
} else {
// No need to keep unspilled values live.
// These are typically rematerializeable constants like nil,
// or values of a variable that were modified since the last call.
v.Op = OpCopy
v.SetArgs1(v.Args[1])
}
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)
b.Values = append(b.Values, v)
s.advanceUses(v)
continue
}
@ -1311,20 +1328,25 @@ func (s *regAllocState) regalloc(f *Func) {
// Start with live at end.
for _, li := range s.live[ss.ID] {
if s.isLoopSpillCandidate(loop, s.orig[li.ID]) {
// s.live contains original IDs, use s.orig above to map back to *Value
entryCandidates.setBit(li.ID, uint(whichExit))
}
}
// Control can also be live.
if ss.Control != nil && s.isLoopSpillCandidate(loop, ss.Control) {
entryCandidates.setBit(ss.Control.ID, uint(whichExit))
if ss.Control != nil && s.orig[ss.Control.ID] != nil && s.isLoopSpillCandidate(loop, s.orig[ss.Control.ID]) {
entryCandidates.setBit(s.orig[ss.Control.ID].ID, uint(whichExit))
}
// Walk backwards, filling in locally live values, removing those defined.
for i := len(ss.Values) - 1; i >= 0; i-- {
v := ss.Values[i]
entryCandidates.remove(v.ID) // Cannot be an issue, only keeps the sets smaller.
vorig := s.orig[v.ID]
if vorig != nil {
entryCandidates.remove(vorig.ID) // Cannot be an issue, only keeps the sets smaller.
}
for _, a := range v.Args {
if s.isLoopSpillCandidate(loop, a) {
entryCandidates.setBit(a.ID, uint(whichExit))
aorig := s.orig[a.ID]
if aorig != nil && s.isLoopSpillCandidate(loop, aorig) {
entryCandidates.setBit(aorig.ID, uint(whichExit))
}
}
}
@ -1524,7 +1546,7 @@ sinking:
}
if f.pass.stats > 0 {
f.logStat("spills_info",
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")
}
}

View file

@ -7,6 +7,8 @@ package ssa
import (
"fmt"
"math"
"os"
"path/filepath"
)
func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) {
@ -357,3 +359,28 @@ func clobber(v *Value) bool {
// Note: leave v.Block intact. The Block field is used after clobber.
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) {
if ruleFile == nil {
// Open a log file to write log to. We open in append
// mode because all.bash runs the compiler lots of times,
// and we want the concatenation of all of those logs.
// This means, of course, that users need to rm the old log
// to get fresh data.
// TODO: all.bash runs compilers in parallel. Need to synchronize logging somehow?
w, err := os.OpenFile(filepath.Join(os.Getenv("GOROOT"), "src", "rulelog"),
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
ruleFile = w
}
_, err := fmt.Fprintf(ruleFile, "rewrite %s\n", s)
if err != nil {
panic(err)
}
}
var ruleFile *os.File

View file

@ -14,13 +14,13 @@ type sparseEntry struct {
type sparseMap struct {
dense []sparseEntry
sparse []int
sparse []int32
}
// newSparseMap returns a sparseMap that can map
// integers between 0 and n-1 to int32s.
func newSparseMap(n int) *sparseMap {
return &sparseMap{nil, make([]int, n)}
return &sparseMap{dense: nil, sparse: make([]int32, n)}
}
func (s *sparseMap) size() int {
@ -29,14 +29,14 @@ func (s *sparseMap) size() int {
func (s *sparseMap) contains(k ID) bool {
i := s.sparse[k]
return i < len(s.dense) && s.dense[i].key == k
return i < int32(len(s.dense)) && s.dense[i].key == k
}
// get returns the value for key k, or -1 if k does
// not appear in the map.
func (s *sparseMap) get(k ID) int32 {
i := s.sparse[k]
if i < len(s.dense) && s.dense[i].key == k {
if i < int32(len(s.dense)) && s.dense[i].key == k {
return s.dense[i].val
}
return -1
@ -44,12 +44,12 @@ func (s *sparseMap) get(k ID) int32 {
func (s *sparseMap) set(k ID, v int32) {
i := s.sparse[k]
if i < len(s.dense) && s.dense[i].key == k {
if i < int32(len(s.dense)) && s.dense[i].key == k {
s.dense[i].val = v
return
}
s.dense = append(s.dense, sparseEntry{k, v})
s.sparse[k] = len(s.dense) - 1
s.sparse[k] = int32(len(s.dense)) - 1
}
// setBit sets the v'th bit of k's value, where 0 <= v < 32
@ -58,17 +58,17 @@ func (s *sparseMap) setBit(k ID, v uint) {
panic("bit index too large.")
}
i := s.sparse[k]
if i < len(s.dense) && s.dense[i].key == k {
if i < int32(len(s.dense)) && s.dense[i].key == k {
s.dense[i].val |= 1 << v
return
}
s.dense = append(s.dense, sparseEntry{k, 1 << v})
s.sparse[k] = len(s.dense) - 1
s.sparse[k] = int32(len(s.dense)) - 1
}
func (s *sparseMap) remove(k ID) {
i := s.sparse[k]
if i < len(s.dense) && s.dense[i].key == k {
if i < int32(len(s.dense)) && s.dense[i].key == k {
y := s.dense[len(s.dense)-1]
s.dense[i] = y
s.sparse[y.key] = i

View file

@ -9,13 +9,13 @@ package ssa
type sparseSet struct {
dense []ID
sparse []int
sparse []int32
}
// newSparseSet returns a sparseSet that can represent
// integers between 0 and n-1
func newSparseSet(n int) *sparseSet {
return &sparseSet{nil, make([]int, n)}
return &sparseSet{dense: nil, sparse: make([]int32, n)}
}
func (s *sparseSet) cap() int {
@ -28,16 +28,16 @@ func (s *sparseSet) size() int {
func (s *sparseSet) contains(x ID) bool {
i := s.sparse[x]
return i < len(s.dense) && s.dense[i] == x
return i < int32(len(s.dense)) && s.dense[i] == x
}
func (s *sparseSet) add(x ID) {
i := s.sparse[x]
if i < len(s.dense) && s.dense[i] == x {
if i < int32(len(s.dense)) && s.dense[i] == x {
return
}
s.dense = append(s.dense, x)
s.sparse[x] = len(s.dense) - 1
s.sparse[x] = int32(len(s.dense)) - 1
}
func (s *sparseSet) addAll(a []ID) {
@ -54,7 +54,7 @@ func (s *sparseSet) addAllValues(a []*Value) {
func (s *sparseSet) remove(x ID) {
i := s.sparse[x]
if i < len(s.dense) && s.dense[i] == x {
if i < int32(len(s.dense)) && s.dense[i] == x {
y := s.dense[len(s.dense)-1]
s.dense[i] = y
s.sparse[y] = i

View file

@ -4,7 +4,9 @@
package ssa
type sparseTreeNode struct {
import "fmt"
type SparseTreeNode struct {
child *Block
sibling *Block
parent *Block
@ -20,26 +22,39 @@ type sparseTreeNode struct {
entry, exit int32
}
func (s *SparseTreeNode) String() string {
return fmt.Sprintf("[%d,%d]", s.entry, s.exit)
}
func (s *SparseTreeNode) Entry() int32 {
return s.entry
}
func (s *SparseTreeNode) Exit() int32 {
return s.exit
}
const (
// When used to lookup up definitions in a sparse tree,
// these adjustments to a block's entry (+adjust) and
// exit (-adjust) numbers allow a distinction to be made
// between assignments (typically branch-dependent
// conditionals) occurring "before" phi functions, the
// phi functions, and at the bottom of a block.
ADJUST_BEFORE = -1 // defined before phi
ADJUST_TOP = 0 // defined by phi
ADJUST_BOTTOM = 1 // defined within block
// conditionals) occurring "before" the block (e.g., as inputs
// to the block and its phi functions), "within" the block,
// and "after" the block.
AdjustBefore = -1 // defined before phi
AdjustWithin = 0 // defined by phi
AdjustAfter = 1 // defined within block
)
// A sparseTree is a tree of Blocks.
// A SparseTree is a tree of Blocks.
// It allows rapid ancestor queries,
// such as whether one block dominates another.
type sparseTree []sparseTreeNode
type SparseTree []SparseTreeNode
// newSparseTree creates a sparseTree from a block-to-parent map (array indexed by Block.ID)
func newSparseTree(f *Func, parentOf []*Block) sparseTree {
t := make(sparseTree, f.NumBlocks())
// newSparseTree creates a SparseTree from a block-to-parent map (array indexed by Block.ID)
func newSparseTree(f *Func, parentOf []*Block) SparseTree {
t := make(SparseTree, f.NumBlocks())
for _, b := range f.Blocks {
n := &t[b.ID]
if p := parentOf[b.ID]; p != nil {
@ -80,7 +95,7 @@ func newSparseTree(f *Func, parentOf []*Block) sparseTree {
// root left left right right root
// 1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18
func (t sparseTree) numberBlock(b *Block, n int32) int32 {
func (t SparseTree) numberBlock(b *Block, n int32) int32 {
// reserve n for entry-1, assign n+1 to entry
n++
t[b.ID].entry = n
@ -103,19 +118,19 @@ func (t sparseTree) numberBlock(b *Block, n int32) int32 {
// to assign entry and exit numbers in the treewalk, those
// numbers are also consistent with this order (i.e.,
// Sibling(x) has entry number larger than x's exit number).
func (t sparseTree) Sibling(x *Block) *Block {
func (t SparseTree) Sibling(x *Block) *Block {
return t[x.ID].sibling
}
// Child returns a child of x in the dominator tree, or
// nil if there are none. The choice of first child is
// arbitrary but repeatable.
func (t sparseTree) Child(x *Block) *Block {
func (t SparseTree) Child(x *Block) *Block {
return t[x.ID].child
}
// isAncestorEq reports whether x is an ancestor of or equal to y.
func (t sparseTree) isAncestorEq(x, y *Block) bool {
func (t SparseTree) isAncestorEq(x, y *Block) bool {
if x == y {
return true
}
@ -125,7 +140,7 @@ func (t sparseTree) isAncestorEq(x, y *Block) bool {
}
// isAncestor reports whether x is a strict ancestor of y.
func (t sparseTree) isAncestor(x, y *Block) bool {
func (t SparseTree) isAncestor(x, y *Block) bool {
if x == y {
return false
}
@ -134,8 +149,38 @@ func (t sparseTree) isAncestor(x, y *Block) bool {
return xx.entry < yy.entry && yy.exit < xx.exit
}
// maxdomorder returns a value to allow a maximal dominator first sort. maxdomorder(x) < maxdomorder(y) is true
// if x may dominate y, and false if x cannot dominate y.
func (t sparseTree) maxdomorder(x *Block) int32 {
// domorder returns a value for dominator-oriented sorting.
// Block domination does not provide a total ordering,
// but domorder two has useful properties.
// (1) If domorder(x) > domorder(y) then x does not dominate y.
// (2) If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y,
// then x does not dominate z.
// Property (1) means that blocks sorted by domorder always have a maximal dominant block first.
// Property (2) allows searches for dominated blocks to exit early.
func (t SparseTree) domorder(x *Block) int32 {
// Here is an argument that entry(x) provides the properties documented above.
//
// Entry and exit values are assigned in a depth-first dominator tree walk.
// For all blocks x and y, one of the following holds:
//
// (x-dom-y) x dominates y => entry(x) < entry(y) < exit(y) < exit(x)
// (y-dom-x) y dominates x => entry(y) < entry(x) < exit(x) < exit(y)
// (x-then-y) neither x nor y dominates the other and x walked before y => entry(x) < exit(x) < entry(y) < exit(y)
// (y-then-x) neither x nor y dominates the other and y walked before y => entry(y) < exit(y) < entry(x) < exit(x)
//
// entry(x) > entry(y) eliminates case x-dom-y. This provides property (1) above.
//
// For property (2), assume entry(x) < entry(y) and entry(y) < entry(z) and x does not dominate y.
// entry(x) < entry(y) allows cases x-dom-y and x-then-y.
// But by supposition, x does not dominate y. So we have x-then-y.
//
// For contractidion, assume x dominates z.
// Then entry(x) < entry(z) < exit(z) < exit(x).
// But we know x-then-y, so entry(x) < exit(x) < entry(y) < exit(y).
// Combining those, entry(x) < entry(z) < exit(z) < exit(x) < entry(y) < exit(y).
// By supposition, entry(y) < entry(z), which allows cases y-dom-z and y-then-z.
// y-dom-z requires entry(y) < entry(z), but we have entry(z) < entry(y).
// y-then-z requires exit(y) < entry(z), but we have entry(z) < exit(y).
// We have a contradiction, so x does not dominate z, as required.
return t[x.ID].entry
}

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