[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 misc/cgo/testso/main
src/cmd/cgo/zdefaultcc.go src/cmd/cgo/zdefaultcc.go
src/cmd/go/zdefaultcc.go src/cmd/go/zdefaultcc.go
src/cmd/go/zosarch.go
src/cmd/internal/obj/zbootstrap.go src/cmd/internal/obj/zbootstrap.go
src/go/build/zcgo.go src/go/build/zcgo.go
src/go/doc/headscan src/go/doc/headscan

90
AUTHORS
View file

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

View file

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

View file

@ -89,7 +89,7 @@ gofmt</a> command with more general options.</td>
</tr> </tr>
<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>&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>Vet examines Go source code and reports suspicious constructs, such as Printf <td>Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string.</td> 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 The first line of the change description is conventionally a one-line
summary of the change, prefixed by the primary affected package, summary of the change, prefixed by the primary affected package,
and is used as the subject for code review mail. and is used as the subject for code review mail.
The rest of the It should complete the sentence "This change modifies Go to _____."
description elaborates and should provide context for the The rest of the description elaborates and should provide context for the
change and explain what it does. 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 there is a helpful reference, mention it here.
If you've fixed an issue, reference it by number with a # before it.
</p> </p>
<p> <p>
@ -364,7 +367,7 @@ After editing, the template might now read:
</p> </p>
<pre> <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 The existing implementation has poor numerical properties for
large arguments, so use the McGillicutty algorithm to improve 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 type ByteSlice []byte
func (slice ByteSlice) Append(data []byte) []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> </pre>
<p> <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>
<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 There are important differences in the quality of the compilers for the different
architectures. architectures.
</p> </p>
@ -63,19 +63,19 @@ architectures.
<code>arm64</code> (<code>AArch64</code>) <code>arm64</code> (<code>AArch64</code>)
</dt> </dt>
<dd> <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> </dd>
<dt> <dt>
<code>ppc64, ppc64le</code> (64-bit PowerPC big- and little-endian) <code>ppc64, ppc64le</code> (64-bit PowerPC big- and little-endian)
</dt> </dt>
<dd> <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> </dd>
<dt> <dt>
<code>mips64, mips64le</code> (64-bit MIPS big- and little-endian) <code>mips64, mips64le</code> (64-bit MIPS big- and little-endian)
</dt> </dt>
<dd> <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> </dd>
</dl> </dl>

View file

@ -221,7 +221,7 @@ and building a simple program, as follows.
<p> <p>
Create a directory to contain your <a href="code.html#Workspaces">workspace</a>, Create a directory to contain your <a href="code.html#Workspaces">workspace</a>,
<code class="testUnix">$HOME/work</code> <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 for example, and set the <code>GOPATH</code> environment
variable to point to that location. variable to point to that location.
</p> </p>
@ -231,7 +231,7 @@ $ <b>export GOPATH=$HOME/work</b>
</pre> </pre>
<pre class="testWindows" style="display: none"> <pre class="testWindows" style="display: none">
C:\&gt; <b>set GOPATH=%HOME%\work</b> C:\&gt; <b>set GOPATH=C:\work</b>
</pre> </pre>
<p> <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, 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() { func main() {

View file

@ -45,6 +45,13 @@ expect issue13129.go C.ushort
check issue13423.go check issue13423.go
expect issue13635.go C.uchar C.schar C.ushort C.uint C.ulong C.longlong C.ulonglong C.complexfloat C.complexdouble 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 if ! go run ptr.go; then
exit 1 exit 1
fi fi

View file

@ -79,14 +79,12 @@ func init() {
} }
if GOOS == "darwin" { if GOOS == "darwin" {
cc = append(cc, "-Wl,-no_pie")
// For Darwin/ARM. // For Darwin/ARM.
// TODO(crawshaw): can we do better? // TODO(crawshaw): can we do better?
cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
} }
libgodir = GOOS + "_" + GOARCH libgodir = GOOS + "_" + GOARCH
if GOOS == "darwin" && GOARCH == "arm" { if GOOS == "darwin" && (GOARCH == "arm" || GOARCH == "arm64") {
libgodir = GOOS + "_" + GOARCH + "_shared" libgodir = GOOS + "_" + GOARCH + "_shared"
} }
cc = append(cc, "-I", filepath.Join("pkg", libgodir)) 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 // Test raising SIGIO on a C thread with an alternate signal stack
// when there is a Go signal handler for SIGIO. // when there is a Go signal handler for SIGIO.
static void* thread1(void* arg) { static void* thread1(void* arg __attribute__ ((unused))) {
pthread_t* ptid = (pthread_t*)(arg);
stack_t ss; stack_t ss;
int i; int i;
stack_t nss; stack_t nss;
@ -65,7 +64,7 @@ static void* thread1(void* arg) {
// Send ourselves a SIGIO. This will be caught by the Go // Send ourselves a SIGIO. This will be caught by the Go
// signal handler which should forward to the C signal // signal handler which should forward to the C signal
// handler. // handler.
i = pthread_kill(*ptid, SIGIO); i = pthread_kill(pthread_self(), SIGIO);
if (i != 0) { if (i != 0) {
fprintf(stderr, "pthread_kill: %s\n", strerror(i)); fprintf(stderr, "pthread_kill: %s\n", strerror(i));
exit(EXIT_FAILURE); 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 // 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. // alternate signal stack when there is a Go signal handler for SIGIO.
static void* thread2(void* arg) { static void* thread2(void* arg __attribute__ ((unused))) {
pthread_t* ptid = (pthread_t*)(arg);
stack_t ss; stack_t ss;
int i; int i;
int oldcount; int oldcount;
pthread_t tid;
stack_t nss; stack_t nss;
// Set up an alternate signal stack for this thread. // 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 // Call a Go function that will call a C function to send us a
// SIGIO. // SIGIO.
GoRaiseSIGIO(ptid); tid = pthread_self();
GoRaiseSIGIO(&tid);
// Wait until the signal has been delivered. // Wait until the signal has been delivered.
i = 0; i = 0;
@ -161,7 +161,7 @@ int main(int argc, char **argv) {
// Tell the Go library to start looking for SIGIO. // Tell the Go library to start looking for SIGIO.
GoCatchSIGIO(); GoCatchSIGIO();
i = pthread_create(&tid, NULL, thread1, (void*)(&tid)); i = pthread_create(&tid, NULL, thread1, NULL);
if (i != 0) { if (i != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(i)); fprintf(stderr, "pthread_create: %s\n", strerror(i));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -173,7 +173,7 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
i = pthread_create(&tid, NULL, thread2, (void*)(&tid)); i = pthread_create(&tid, NULL, thread2, NULL);
if (i != 0) { if (i != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(i)); fprintf(stderr, "pthread_create: %s\n", strerror(i));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View file

@ -134,6 +134,26 @@ if test "$tsan" = "yes"; then
status=1 status=1
fi 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 rm -f $err
fi 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,23 +27,23 @@ go src=..
internal internal
objfile objfile
objfile.go objfile.go
unvendor
golang.org
x
arch
arm
armasm
testdata
+
x86
x86asm
testdata
+
gofmt gofmt
gofmt.go gofmt.go
gofmt_test.go gofmt_test.go
testdata testdata
+ +
vendor
golang.org
x
arch
arm
armasm
testdata
+
x86
x86asm
testdata
+
archive archive
tar tar
testdata testdata

View file

@ -21,10 +21,8 @@ import (
"time" "time"
) )
// Header type flags.
const ( const (
blockSize = 512
// Types
TypeReg = '0' // regular file TypeReg = '0' // regular file
TypeRegA = '\x00' // regular file TypeRegA = '\x00' // regular file
TypeLink = '1' // hard link TypeLink = '1' // hard link
@ -61,12 +59,6 @@ type Header struct {
Xattrs map[string]string 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. // FileInfo returns an os.FileInfo for the Header.
func (h *Header) FileInfo() os.FileInfo { func (h *Header) FileInfo() os.FileInfo {
return headerFileInfo{h} return headerFileInfo{h}
@ -279,33 +271,6 @@ func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
return h, nil 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 { func isASCII(s string) bool {
for _, c := range s { for _, c := range s {
if c >= 0x80 { 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

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
/* /*
Package zip provides support for reading and writing ZIP archives. 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. 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") b.Fatal("ioutil.Discard doesn't support ReaderFrom")
} }
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
r.Seek(0, 0) r.Seek(0, io.SeekStart)
srcReader.Reset(onlyReader{r}) srcReader.Reset(onlyReader{r})
n, err := srcReader.WriteTo(ioutil.Discard) n, err := srcReader.WriteTo(ioutil.Discard)
if err != nil { if err != nil {

View file

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

View file

@ -1089,6 +1089,8 @@ func (p *Package) gccMachine() []string {
return []string{"-m31"} return []string{"-m31"}
case "s390x": case "s390x":
return []string{"-m64"} return []string{"-m64"}
case "mips64", "mips64le":
return []string{"-mabi=64"}
} }
return nil return nil
} }
@ -1241,12 +1243,20 @@ func (p *Package) gccErrors(stdin []byte) string {
// TODO(rsc): require failure // TODO(rsc): require failure
args := p.gccCmd() 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 { 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) os.Stderr.Write(stdin)
fmt.Fprint(os.Stderr, "EOF\n") fmt.Fprint(os.Stderr, "EOF\n")
} }
stdout, stderr, _ := run(stdin, args) stdout, stderr, _ := run(stdin, nargs)
if *debugGcc { if *debugGcc {
os.Stderr.Write(stdout) os.Stderr.Write(stdout)
os.Stderr.Write(stderr) os.Stderr.Write(stderr)

View file

@ -175,10 +175,11 @@ func (p *Package) writeDefs() {
} }
fmt.Fprintf(fgo2, "\n") fmt.Fprintf(fgo2, "\n")
callsMalloc := false
for _, key := range nameKeys(p.Name) { for _, key := range nameKeys(p.Name) {
n := p.Name[key] n := p.Name[key]
if n.FuncType != nil { if n.FuncType != nil {
p.writeDefsFunc(fgo2, n) p.writeDefsFunc(fgo2, n, &callsMalloc)
} }
} }
@ -189,6 +190,12 @@ func (p *Package) writeDefs() {
} else { } else {
p.writeExports(fgo2, fm, fgcc, fgcch) 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 { if err := fgcc.Close(); err != nil {
fatalf("%s", err) fatalf("%s", err)
} }
@ -352,7 +359,7 @@ func (p *Package) structType(n *Name) (string, int64) {
return buf.String(), off 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 name := n.Go
gtype := n.FuncType.Go gtype := n.FuncType.Go
void := gtype.Results == nil || len(gtype.Results.List) == 0 void := gtype.Results == nil || len(gtype.Results.List) == 0
@ -441,6 +448,9 @@ func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name) {
if inProlog { if inProlog {
fmt.Fprint(fgo2, builtinDefs[name]) fmt.Fprint(fgo2, builtinDefs[name])
if strings.Contains(builtinDefs[name], "_cgo_cmalloc") {
*callsMalloc = true
}
return return
} }
@ -560,6 +570,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// Gcc wrapper unpacks the C argument struct // Gcc wrapper unpacks the C argument struct
// and calls the actual C function. // and calls the actual C function.
fmt.Fprintf(fgcc, "CGO_NO_SANITIZE_THREAD\n")
if n.AddError { if n.AddError {
fmt.Fprintf(fgcc, "int\n") fmt.Fprintf(fgcc, "int\n")
} else { } 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 // wrapper, we can't refer to the function, since the reference is in
// a different file. // a different file.
func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) { func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
fmt.Fprintf(fgcc, "CGO_NO_SANITIZE_THREAD\n")
if t := n.FuncType.Result; t != nil { if t := n.FuncType.Result; t != nil {
fmt.Fprintf(fgcc, "%s\n", t.C.String()) fmt.Fprintf(fgcc, "%s\n", t.C.String())
} else { } else {
@ -710,11 +722,13 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
p.writeExportHeader(fgcch) p.writeExportHeader(fgcch)
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") 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, "#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 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 __SIZE_TYPE__ _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\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) fmt.Fprintf(fgcc, "%s\n", tsanProlog)
for _, exp := range p.ExpFunc { 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(fgcch, "\nextern %s;\n", s)
fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName) 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%s\n", s)
fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "{\n")
fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\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.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, "\n") fmt.Fprint(fgcc, "\nCGO_NO_SANITIZE_THREAD\n")
fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams) fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams)
if resultCount > 0 { if resultCount > 0 {
fmt.Fprintf(fgcc, "\t%s r;\n", cRet) fmt.Fprintf(fgcc, "\t%s r;\n", cRet)
@ -1304,11 +1319,14 @@ extern char* _cgo_topofstack(void);
// Prologue defining TSAN functions in C. // Prologue defining TSAN functions in C.
const noTsanProlog = ` const noTsanProlog = `
#define CGO_NO_SANITIZE_THREAD
#define _cgo_tsan_acquire() #define _cgo_tsan_acquire()
#define _cgo_tsan_release() #define _cgo_tsan_release()
` `
const yesTsanProlog = ` const yesTsanProlog = `
#define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread))
long long _cgo_sync __attribute__ ((common)); long long _cgo_sync __attribute__ ((common));
extern void __tsan_acquire(void*); extern void __tsan_acquire(void*);
@ -1346,9 +1364,6 @@ const goProlog = `
//go:linkname _cgo_runtime_cgocall runtime.cgocall //go:linkname _cgo_runtime_cgocall runtime.cgocall
func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32 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 //go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr) func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr)
@ -1360,10 +1375,8 @@ func _cgoCheckResult(interface{})
` `
const gccgoGoProlog = ` const gccgoGoProlog = `
//extern runtime.cgoCheckPointer
func _cgoCheckPointer(interface{}, ...interface{}) interface{} func _cgoCheckPointer(interface{}, ...interface{}) interface{}
//extern runtime.cgoCheckResult
func _cgoCheckResult(interface{}) func _cgoCheckResult(interface{})
` `
@ -1396,7 +1409,7 @@ func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
const cStringDef = ` const cStringDef = `
func _Cfunc_CString(s string) *_Ctype_char { 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) pp := (*[1<<30]byte)(p)
copy(pp[:], s) copy(pp[:], s)
pp[len(s)] = 0 pp[len(s)] = 0
@ -1406,7 +1419,7 @@ func _Cfunc_CString(s string) *_Ctype_char {
const cBytesDef = ` const cBytesDef = `
func _Cfunc_CBytes(b []byte) unsafe.Pointer { 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) pp := (*[1<<30]byte)(p)
copy(pp[:], b) copy(pp[:], b)
return p return p
@ -1415,7 +1428,7 @@ func _Cfunc_CBytes(b []byte) unsafe.Pointer {
const cMallocDef = ` const cMallocDef = `
func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer { 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, "_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 { func (p *Package) cPrologGccgo() string {
return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1), return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1),
"GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1) "GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1)

View file

@ -60,8 +60,15 @@ Flags:
-installsuffix suffix -installsuffix suffix
Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix
instead of $GOROOT/pkg/$GOOS_$GOARCH. instead of $GOROOT/pkg/$GOOS_$GOARCH.
-l
Disable inlining.
-largemodel -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 -memprofile file
Write memory profile for the compilation to file. Write memory profile for the compilation to file.
-memprofilerate rate -memprofilerate rate

View file

@ -25,18 +25,16 @@ func betypeinit() {
cmpptr = x86.ACMPL cmpptr = x86.ACMPL
} }
if gc.Ctxt.Flag_dynlink { if gc.Ctxt.Flag_dynlink || obj.Getgoos() == "nacl" {
gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15) 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() { 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 gc.Thearch.LinkArch = &x86.Linkamd64
if obj.Getgoarch() == "amd64p32" { if obj.Getgoarch() == "amd64p32" {
gc.Thearch.LinkArch = &x86.Linkamd64p32 gc.Thearch.LinkArch = &x86.Linkamd64p32
@ -51,7 +49,6 @@ func Main() {
gc.Thearch.FREGMIN = x86.REG_X0 gc.Thearch.FREGMIN = x86.REG_X0
gc.Thearch.FREGMAX = x86.REG_X15 gc.Thearch.FREGMAX = x86.REG_X15
gc.Thearch.MAXWIDTH = 1 << 50 gc.Thearch.MAXWIDTH = 1 << 50
gc.Thearch.ReservedRegs = resvd
gc.Thearch.AddIndex = addindex gc.Thearch.AddIndex = addindex
gc.Thearch.Betypeinit = betypeinit 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 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 r1 = *n1
} else { } else {
gc.Regalloc(&r1, t, n1) gc.Regalloc(&r1, t, n1)
@ -229,6 +229,8 @@ func gmove(f *gc.Node, t *gc.Node) {
switch uint32(ft)<<16 | uint32(tt) { switch uint32(ft)<<16 | uint32(tt) {
default: 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)) 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 ( import (
"cmd/compile/internal/gc" "cmd/compile/internal/gc"
"cmd/internal/obj"
"cmd/internal/obj/x86" "cmd/internal/obj/x86"
) )
@ -121,7 +120,7 @@ func BtoR(b uint64) int {
b &= 0xffff b &= 0xffff
if gc.Nacl { if gc.Nacl {
b &^= (1<<(x86.REG_BP-x86.REG_AX) | 1<<(x86.REG_R15-x86.REG_AX)) 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. // BP is part of the calling convention if framepointer_enabled.
b &^= (1 << (x86.REG_BP - x86.REG_AX)) 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)) gc.Gvarkill(v.Aux.(*gc.Node))
case ssa.OpVarLive: case ssa.OpVarLive:
gc.Gvarlive(v.Aux.(*gc.Node)) 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: case ssa.OpAMD64LoweredNilCheck:
// Optimization - if the subsequent block has a load or store // Optimization - if the subsequent block has a load or store
// at the same address, we don't need to issue this instruction. // 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 n = &n1
case gc.ONAME: case gc.ONAME, gc.OINDREG:
if n.Class == gc.PPARAMREF {
var n1 gc.Node
gc.Cgen(n.Name.Heapaddr, &n1)
sclean[nsclean-1] = n1
n = &n1
}
// nothing // nothing
case gc.OINDREG:
break
} }
*lo = *n *lo = *n

View file

@ -55,12 +55,15 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
} }
f.Offset = o f.Offset = o
if f.Nname != nil { if f.Nname != nil {
// this same stackparam logic is in addrescapes // addrescapes has similar code to update these offsets.
// in typecheck.go. usually addrescapes runs after // Usually addrescapes runs after widstruct,
// widstruct, in which case we could drop this, // in which case we could drop this,
// but function closure functions are the exception. // but function closure functions are the exception.
if f.Nname.Name.Param.Stackparam != nil { // NOTE(rsc): This comment may be stale.
f.Nname.Name.Param.Stackparam.Xoffset = o // 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 f.Nname.Xoffset = 0
} else { } else {
f.Nname.Xoffset = o 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) // (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 The export data is a serialized description of the graph of exported
"objects": constants, types, variables, and functions. In general, "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 predeclared types (int, string, error, unsafe.Pointer, etc.). This way
they are automatically encoded with a known and fixed type index. 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 The export data starts with a single byte indicating the encoding format
(compact, or with debugging information), followed by a version string (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 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 field needs to be encoded, a symmetric change can be made to exporter and
importer. 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 package gc
@ -125,6 +162,17 @@ const exportVersion = "v0"
// Leave for debugging. // Leave for debugging.
const exportInlined = true // default: true 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 { type exporter struct {
out *bufio.Writer out *bufio.Writer
@ -159,6 +207,10 @@ func export(out *bufio.Writer, trace bool) int {
trace: trace, 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 // first byte indicates low-level encoding format
var format byte = 'c' // compact var format byte = 'c' // compact
if debugFormat { if debugFormat {
@ -166,6 +218,12 @@ func export(out *bufio.Writer, trace bool) int {
} }
p.rawByte(format) p.rawByte(format)
format = 'n' // track named types only
if trackAllTypes {
format = 'a'
}
p.rawByte(format)
// posInfo exported or not? // posInfo exported or not?
p.bool(p.posInfoFormat) 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 // otherwise, remember the type, write the type tag (< 0) and type data
if p.trace { if trackAllTypes {
p.tracef("T%d = {>\n", len(p.typIndex)) if p.trace {
defer p.tracef("<\n} ") p.tracef("T%d = {>\n", len(p.typIndex))
defer p.tracef("<\n} ")
}
p.typIndex[t] = len(p.typIndex)
} }
p.typIndex[t] = len(p.typIndex)
// pick off named types // pick off named types
if tsym := t.Sym; tsym != nil { 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. // Predeclared types should have been found in the type map.
if t.Orig == t { if t.Orig == t {
Fatalf("exporter: predeclared type missing from type map?") 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 // print symbol with Vargen number or not as desired
name := s.Name name := s.Name
if strings.Contains(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 // 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 // but instead of emitting the information textually, emit the node tree in
// binary form. // binary form.
// TODO(gri) Improve tracing output. The current format is difficult to read.
// stmtList may emit more (or fewer) than len(list) nodes. // stmtList may emit more (or fewer) than len(list) nodes.
func (p *exporter) stmtList(list Nodes) { func (p *exporter) stmtList(list Nodes) {
if p.trace { if p.trace {
@ -1088,6 +1155,16 @@ func (p *exporter) expr(n *Node) {
defer p.tracef(") ") 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) { for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) {
n = n.Left n = n.Left
} }
@ -1117,15 +1194,13 @@ func (p *exporter) expr(n *Node) {
// Special case: name used as local variable in export. // Special case: name used as local variable in export.
// _ becomes ~b%d internally; print as _ for export // _ becomes ~b%d internally; print as _ for export
if n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' { if n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
// case 0: mapped to ONAME
p.op(ONAME) p.op(ONAME)
p.bool(true) // indicate blank identifier p.string("_") // inlined and customized version of p.sym(n)
break break
} }
if n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 { if n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 {
// case 1: mapped to OPACK p.op(ONAME)
p.op(OPACK)
p.sym(n) p.sym(n)
break break
} }
@ -1134,24 +1209,18 @@ func (p *exporter) expr(n *Node) {
// but for export, this should be rendered as (*pkg.T).meth. // 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. // 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 { if n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME {
// case 2: mapped to ONAME p.op(OXDOT)
p.op(ONAME) p.expr(n.Left) // n.Left.Op == OTYPE
// 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.fieldSym(n.Right.Sym, true) p.fieldSym(n.Right.Sym, true)
break break
} }
// case 3: mapped to OPACK p.op(ONAME)
p.op(OPACK)
p.sym(n) // fallthrough inlined here
case OPACK, ONONAME:
p.op(op)
p.sym(n) p.sym(n)
// case OPACK, ONONAME:
// should have been resolved by typechecking - handled by default case
case OTYPE: case OTYPE:
p.op(OTYPE) p.op(OTYPE)
if p.bool(n.Type == nil) { if p.bool(n.Type == nil) {
@ -1160,14 +1229,14 @@ func (p *exporter) expr(n *Node) {
p.typ(n.Type) p.typ(n.Type)
} }
case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
panic("unreachable") // should have been resolved by typechecking // should have been resolved by typechecking - handled by default case
// case OCLOSURE: // case OCLOSURE:
// unimplemented - handled by default case // unimplemented - handled by default case
// case OCOMPLIT: // case OCOMPLIT:
// unimplemented - handled by default case // should have been resolved by typechecking - handled by default case
case OPTRLIT: case OPTRLIT:
p.op(OPTRLIT) p.op(OPTRLIT)
@ -1176,16 +1245,12 @@ func (p *exporter) expr(n *Node) {
case OSTRUCTLIT: case OSTRUCTLIT:
p.op(OSTRUCTLIT) p.op(OSTRUCTLIT)
if !p.bool(n.Implicit) { p.typ(n.Type)
p.typ(n.Type)
}
p.elemList(n.List) // special handling of field names p.elemList(n.List) // special handling of field names
case OARRAYLIT, OMAPLIT: case OARRAYLIT, OMAPLIT:
p.op(op) p.op(OCOMPLIT)
if !p.bool(n.Implicit) { p.typ(n.Type)
p.typ(n.Type)
}
p.exprList(n.List) p.exprList(n.List)
case OKEY: case OKEY:
@ -1198,9 +1263,6 @@ func (p *exporter) expr(n *Node) {
case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
p.op(OXDOT) p.op(OXDOT)
p.expr(n.Left) p.expr(n.Left)
if n.Sym == nil {
panic("unreachable") // can this happen during export?
}
p.fieldSym(n.Sym, true) p.fieldSym(n.Sym, true)
case ODOTTYPE, ODOTTYPE2: case ODOTTYPE, ODOTTYPE2:
@ -1231,26 +1293,35 @@ func (p *exporter) expr(n *Node) {
p.expr(max) p.expr(max)
case OCOPY, OCOMPLEX: case OCOPY, OCOMPLEX:
// treated like other builtin calls (see e.g., OREAL)
p.op(op) p.op(op)
p.expr(n.Left) p.expr(n.Left)
p.expr(n.Right) p.expr(n.Right)
p.op(OEND)
case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR: case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR:
p.op(OCONV) p.op(OCONV)
p.typ(n.Type) p.typ(n.Type)
if p.bool(n.Left != nil) { if n.Left != nil {
p.expr(n.Left) p.expr(n.Left)
p.op(OEND)
} else { } 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: case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
p.op(op) p.op(op)
if p.bool(n.Left != nil) { if n.Left != nil {
p.expr(n.Left) p.expr(n.Left)
p.op(OEND)
} else { } 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) p.bool(n.Isddd)
} else if n.Isddd {
Fatalf("exporter: unexpected '...' with %s call", opnames[op])
} }
case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG: case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
@ -1306,7 +1377,8 @@ func (p *exporter) expr(n *Node) {
p.op(ODCLCONST) p.op(ODCLCONST)
default: 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 { switch op := n.Op; op {
case ODCL: case ODCL:
p.op(ODCL) p.op(ODCL)
switch n.Left.Class &^ PHEAP { switch n.Left.Class {
case PPARAM, PPARAMOUT, PAUTO: case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP:
// TODO(gri) when is this not PAUTO? // TODO(gri) when is this not PAUTO?
// Also, originally this didn't look like // Also, originally this didn't look like
// the default case. Investigate. // the default case. Investigate.
@ -1370,10 +1442,7 @@ func (p *exporter) stmt(n *Node) {
p.expr(n.Right) p.expr(n.Right)
} }
case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
fallthrough
case OAS2:
p.op(OAS2) p.op(OAS2)
p.exprList(n.List) p.exprList(n.List)
p.exprList(n.Rlist) p.exprList(n.Rlist)
@ -1382,9 +1451,8 @@ func (p *exporter) stmt(n *Node) {
p.op(ORETURN) p.op(ORETURN)
p.exprList(n.List) p.exprList(n.List)
case ORETJMP: // case ORETJMP:
// generated by compiler for trampolin routines - not exported // unreachable - generated by compiler for trampolin routines
panic("unreachable")
case OPROC, ODEFER: case OPROC, ODEFER:
p.op(op) p.op(op)
@ -1420,19 +1488,18 @@ func (p *exporter) stmt(n *Node) {
p.stmtList(n.List) p.stmtList(n.List)
p.stmtList(n.Nbody) p.stmtList(n.Nbody)
case OFALL: case OFALL, OXFALL:
op = OXFALL p.op(OXFALL)
fallthrough
case OBREAK, OCONTINUE, OGOTO, OXFALL: case OBREAK, OCONTINUE:
p.op(op) p.op(op)
p.exprsOrNil(n.Left, nil) p.exprsOrNil(n.Left, nil)
case OEMPTY: case OEMPTY:
// nothing to emit // nothing to emit
case OLABEL: case OGOTO, OLABEL:
p.op(OLABEL) p.op(op)
p.expr(n.Left) p.expr(n.Left)
default: 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) { func (p *exporter) sym(n *Node) {
s := n.Sym s := n.Sym
if s.Pkg != nil { if s.Pkg != nil {

View file

@ -3,7 +3,8 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Binary package import. // 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 package gc
@ -24,10 +25,11 @@ type importer struct {
buf []byte // reused for reading strings buf []byte // reused for reading strings
// object lists, in order of deserialization // object lists, in order of deserialization
strList []string strList []string
pkgList []*Pkg pkgList []*Pkg
typList []*Type typList []*Type
funcList []*Node // nil entry means already declared funcList []*Node // nil entry means already declared
trackAllTypes bool
// for delayed type verification // for delayed type verification
cmpList []struct{ pt, t *Type } 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) Fatalf("importer: invalid encoding format in export data: got %q; want 'c' or 'd'", format)
} }
p.trackAllTypes = p.rawByte() == 'a'
p.posInfoFormat = p.bool() p.posInfoFormat = p.bool()
// --- generic export data --- // --- generic export data ---
@ -100,7 +104,9 @@ func Import(in *bufio.Reader) {
// --- compiler-specific export data --- // --- compiler-specific export data ---
// read compiler-specific flags // 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 // phase 2
objcount = 0 objcount = 0
@ -230,7 +236,7 @@ func (p *importer) pkg() *Pkg {
// an empty path denotes the package we are currently importing; // an empty path denotes the package we are currently importing;
// it must be the first package we see // it must be the first package we see
if (path == "") != (len(p.pkgList) == 0) { 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 pkg := importpkg
@ -331,7 +337,9 @@ func (p *importer) pos() {
func (p *importer) newtyp(etype EType) *Type { func (p *importer) newtyp(etype EType) *Type {
t := typ(etype) t := typ(etype)
p.typList = append(p.typList, t) if p.trackAllTypes {
p.typList = append(p.typList, t)
}
return t return t
} }
@ -389,7 +397,13 @@ func (p *importer) typ() *Type {
// read underlying type // read underlying type
// parser.go:hidden_type // parser.go:hidden_type
t0 := p.typ() 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 // interfaces don't have associated methods
if t0.IsInterface() { if t0.IsInterface() {
@ -788,16 +802,11 @@ func (p *importer) node() *Node {
return n return n
case ONAME: 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()) return mkname(p.sym())
// case OPACK, ONONAME:
// unreachable - should have been resolved by typechecking
case OTYPE: case OTYPE:
if p.bool() { if p.bool() {
return mkname(p.sym()) return mkname(p.sym())
@ -810,12 +819,9 @@ func (p *importer) node() *Node {
// case OCLOSURE: // case OCLOSURE:
// unimplemented // unimplemented
// case OCOMPLIT:
// unimplemented
case OPTRLIT: case OPTRLIT:
n := p.expr() n := p.expr()
if !p.bool() /* !implicit, i.e. '&' operator*/ { if !p.bool() /* !implicit, i.e. '&' operator */ {
if n.Op == OCOMPLIT { if n.Op == OCOMPLIT {
// Special case for &T{...}: turn into (*T){...}. // Special case for &T{...}: turn into (*T){...}.
n.Right = Nod(OIND, n.Right, nil) n.Right = Nod(OIND, n.Right, nil)
@ -827,18 +833,15 @@ func (p *importer) node() *Node {
return n return n
case OSTRUCTLIT: case OSTRUCTLIT:
n := Nod(OCOMPLIT, nil, nil) n := Nod(OCOMPLIT, nil, typenod(p.typ()))
if !p.bool() { n.List.Set(p.elemList()) // special handling of field names
n.Right = typenod(p.typ())
}
n.List.Set(p.elemList())
return n return n
case OARRAYLIT, OMAPLIT: // case OARRAYLIT, OMAPLIT:
n := Nod(OCOMPLIT, nil, nil) // unreachable - mapped to case OCOMPLIT below by exporter
if !p.bool() {
n.Right = typenod(p.typ()) case OCOMPLIT:
} n := Nod(OCOMPLIT, nil, typenod(p.typ()))
n.List.Set(p.exprList()) n.List.Set(p.exprList())
return n return n
@ -854,14 +857,7 @@ func (p *importer) node() *Node {
case OXDOT: case OXDOT:
// see parser.new_dotname // see parser.new_dotname
obj := p.expr() return NodSym(OXDOT, p.expr(), p.fieldSym())
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)
// case ODOTTYPE, ODOTTYPE2: // case ODOTTYPE, ODOTTYPE2:
// unreachable - mapped to case ODOTTYPE below by exporter // unreachable - mapped to case ODOTTYPE below by exporter
@ -891,29 +887,18 @@ func (p *importer) node() *Node {
n.SetSliceBounds(low, high, max) n.SetSliceBounds(low, high, max)
return n 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: // case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR:
// unreachable - mapped to OCONV case below by exporter // unreachable - mapped to OCONV case below by exporter
case OCONV: case OCONV:
n := Nod(OCALL, typenod(p.typ()), nil) n := Nod(OCALL, typenod(p.typ()), nil)
if p.bool() { n.List.Set(p.exprList())
n.List.Set1(p.expr())
} else {
n.List.Set(p.exprList())
}
return n 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) n := builtinCall(op)
if p.bool() { n.List.Set(p.exprList())
n.List.Set1(p.expr()) if op == OAPPEND {
} else {
n.List.Set(p.exprList())
n.Isddd = p.bool() n.Isddd = p.bool()
} }
return n return n
@ -1053,6 +1038,7 @@ func (p *importer) node() *Node {
case OXCASE: case OXCASE:
markdcl() markdcl()
n := Nod(OXCASE, nil, nil) n := Nod(OXCASE, nil, nil)
n.Xoffset = int64(block)
n.List.Set(p.exprList()) n.List.Set(p.exprList())
// TODO(gri) eventually we must declare variables for type switch // TODO(gri) eventually we must declare variables for type switch
// statements (type switch statements are not yet exported) // statements (type switch statements are not yet exported)
@ -1063,23 +1049,32 @@ func (p *importer) node() *Node {
// case OFALL: // case OFALL:
// unreachable - mapped to OXFALL case below by exporter // 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() left, _ := p.exprsOrNil()
if left != nil {
left = newname(left.Sym)
}
return Nod(op, left, nil) return Nod(op, left, nil)
// case OEMPTY: // case OEMPTY:
// unreachable - not emitted by exporter // unreachable - not emitted by exporter
case OLABEL: case OGOTO, OLABEL:
n := Nod(OLABEL, p.expr(), nil) n := Nod(op, newname(p.expr().Sym), nil)
n.Left.Sym = dclstack // context, for goto restrictions n.Sym = dclstack // context, for goto restrictions
return n return n
case OEND: case OEND:
return nil return nil
default: 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 panic("unreachable") // satisfy compiler
} }
} }

View file

@ -3,106 +3,108 @@
package gc package gc
const runtimeimport = "" + const runtimeimport = "" +
"c\x00\x03v0\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00\x01" + "cn\x00\x03v0\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00" +
"\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15panic" + "\x01\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15pani" +
"divide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00\t" + "cdivide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00" +
"\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11gor" + "\t\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11go" +
"ecover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13printf" + "recover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13print" +
"loat\x00\x01\x1a\x00\x00\t\x0fprintint\x00\x01\n\x00\x00\t\x0fprinthex\x00\x01\x14\x00\x00\t" + "float\x00\x01\x1a\x00\x00\t\x0fprintint\x00\x01\n\x00\x00\t\x0fprinthex\x00\x01\x14\x00\x00" +
"\x11printuint\x00\x01\x14\x00\x00\t\x17printcomplex\x00\x01\x1e\x00\x00\t\x15prin" + "\t\x11printuint\x00\x01\x14\x00\x00\t\x17printcomplex\x00\x01\x1e\x00\x00\t\x15pri" +
"tstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printif" + "ntstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printi" +
"ace\x00\x01:\x00\x00\t\x13printeface\x00\x01:\x00\x00\t\x13printslice\x00\x01:" + "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" + ":\x00\x00\t\rprintnl\x00\x00\x00\t\rprintsp\x00\x00\x00\t\x11printlock\x00\x00" +
"\t\x15printunlock\x00\x00\x00\t\x19concatstring2\x00\x05\x17\x0f@\"\x00 \x00" + "\x00\t\x15printunlock\x00\x00\x00\t\x19concatstring2\x00\x05\x17\x0f@\"\x00 " +
" \x00\x01 \x00\t\x19concatstring3\x00\a\x17\x0f@\"\x00 \x00 \x00 \x00\x01 \x00\t\x19co" + "\x00 \x00\x01 \x00\t\x19concatstring3\x00\a\x17\x0f@\"\x00 \x00 \x00 \x00\x01 \x00\t\x19c" +
"ncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstr" + "oncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatst" +
"ing5\x00\v\x17\x0f@\"\x00 \x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstrings\x00" + "ring5\x00\v\x17\x0f@\"\x00 \x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstrings" +
"\x03\x17\x0f@\"\x00\x11 \x00\x01 \x00\t\x11cmpstring\x00\x03 \x00 \x00\x01\x02\x00\t\x0feqstri" + "\x00\x03\x17\x0f@\"\x00\x11 \x00\x01 \x00\t\x11cmpstring\x00\x03 \x00 \x00\x01\x02\x00\t\x0feqstr" +
"ng\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!slic" + "ing\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!sli" +
"ebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebytetos" + "cebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebyteto" +
"tringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostring\x00\x03\x17\x0f@" + "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\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" + "\"\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t!string" +
"oslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x13stringiter\x00\x03 " + "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\r" + " \x00\x02\x00\x01\x02\x00\t\x15stringiter2\x00\x03 \x00\x02\x00\x04\x02\rretk·1\x00\x00|S" +
"retv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00\x00" + "\rretv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00" +
"\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dslicestring" + "\x00\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dslicestrin" +
"copy\x00\x04:^\x00\x00:`\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00\x02" + "gcopy\x00\x04:^\x00\x00:`\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00" +
":\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\b\x00\x00:\relem·3\x00\x00\x02:l" + "\x02:\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\b\x00\x00:\relem·3\x00\x00\x02:" +
"\x00\x00\t\rconvT2E\x00\x06\x17\"\b\x00\x00>p\x00\x00>\vbuf·4\x00\x00\x02:l\x00\x00\t\rc" + "l\x00\x00\t\rconvT2E\x00\x06\x17\"\b\x00\x00\x17:p\x00\x00\x17:\vbuf·4\x00\x00\x02:l\x00\x00" +
"onvT2I\x00\x06\x17\"\vtab·2\x00\x00>p\x00\x00>t\x00\x00\x02:l\x00\x00\t\x11assert" + "\t\rconvT2I\x00\x06\x17\"\vtab·2\x00\x00\x17:p\x00\x00\x17:t\x00\x00\x02:l\x00\x00\t\x11a" +
"E2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00>\vret·3\x00\x00\x00\t" + "ssertE2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00\x17:\vret\xc2" +
"\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00>\vret·4\x00\x00" + "\xb73\x00\x00\x00\t\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00\x17:\vr" +
"\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assert" + "et·4\x00\x00\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00" +
"E2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertE2T\x00\x06\x17\"|" + "\t\x13assertE2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11asser" +
"|\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01" + "tE2T\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b" +
"\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13asse" + "\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00\x17" +
"rtI2E2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2I\x00\x06\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" +
"\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertI2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>" + "\x11assertI2I\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertI2I" +
"\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13as" + "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" +
"sertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x17panicdotty" + "\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01" +
"pe\x00\x06\x17\"\rhave·1\x00\x00\x9a\x01\rwant·2\x00\x00\x9a\x01\x84\x01\x00\x00\x00\t\rifa" + "\x00\x00\x01\x00\x00\t\x17panicdottype\x00\x06\x17\"\rhave·1\x00\x00\x17\"\rwant" +
"ceeq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00\x00\x02\x00l\x00\x00\t\refaceeq\x00\x04" + "·2\x00\x00\x17\"\x84\x01\x00\x00\x00\t\rifaceeq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00" +
":\xa4\x01\x00\x00:\xa6\x01\x00\x00\x02\x00l\x00\x00\t\rmakemap\x00\b\x17\"\x13mapType·2\x00" + "\x00\x02\x00l\x00\x00\t\refaceeq\x00\x04:\xa4\x01\x00\x00:\xa6\x01\x00\x00\x02\x00l\x00\x00\t\rmakema" +
"\x00\n\rhint·3\x00\x00>\x11mapbuf·4\x00\x00>\x17bucketbuf·5\x00" + "p\x00\b\x17\"\x13mapType·2\x00\x00\n\rhint·3\x00\x00\x17:\x11mapbuf·" +
"\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapaccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rh" + "4\x00\x00\x17:\x17bucketbuf·5\x00\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapa" +
"map·3\x00\x00>\vkey·4\x00\x00\x02>\vval·1\x00\x00\t!mapaccess" + "ccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rhmap·3\x00\x00\x17:\vkey·4\x00\x00\x02\x17" +
"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" + ":\vval·1\x00\x00\t!mapaccess1_fast32\x00\x06\x17\"\xac\x01\x00\x00\x1d::" +
"ccess1_fast64\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t" + "\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t!mapaccess1_fast64\x00\x06\x17\"\xac" +
"#mapaccess1_faststr\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02" + "\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t#mapaccess1_fasts" +
">\xbc\x01\x00\x00\t\x1bmapaccess1_fat\x00\b\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00>\xba\x01\x00" + "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" +
"\x00\x17\"\rzero·5\x00\x00\x02>\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapT" + "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" +
"ype·3\x00\x00\x1d::\rhmap·4\x00\x00>\vkey·5\x00\x00\x04>\xbc\x01\x00\x00\x00\rp" + ":\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapType·3\x00\x00\x1d::\rhm" +
"res·2\x00\x00\t!mapaccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01" + "ap·4\x00\x00\x17:\vkey·5\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\rpres·2\x00\x00\t!ma" +
"\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17" + "paccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01" +
"\"\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" + "\x00\x00\x00\xd0\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00" +
"_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" + "\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t#mapaccess2_faststr\x00\x06" +
"\x1bmapaccess2_fat\x00\b\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00>\xce\x01\x00\x00\x17\"\rze" + "\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" +
"ro·6\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapTy" + "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" +
"pe·1\x00\x00\x1d::\rhmap·2\x00\x00>\vkey·3\x00\x00>\vval·4\x00\x00" + "\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapType·1\x00\x00" +
"\x00\t\x15mapiterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\x0fhiter·3\x00" + "\x1d::\rhmap·2\x00\x00\x17:\vkey·3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15ma" +
"\x00\x00\t\x11mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\xe2\x01\x00\x00\x00\t\x15mapi" + "piterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00\t\x11" +
"ternext\x00\x02>\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15cha" + "mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00\x17:\xe2\x01\x00\x00\x00\t\x15mapiter" +
"nType·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv" + "next\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15chanT" +
"1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00>p\x00\x00\x00\t\x11" + "ype·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv1\x00" +
"chanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00>\relem·4" + "\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00\x17:p\x00\x00\x00\t\x11c" +
"\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" + "hanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00\x17:\relem·4" +
"osechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renabled" + "\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" +
"\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04>" + "losechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renable" +
"\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"||" + "d\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04" +
"\x00\x00>\vdst·2\x00\x00>\vsrc·3\x00\x00\x00\t\x1btypedslicecopy\x00" + "\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"" +
"\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selectnbs" + "||\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc·3\x00\x00\x00\t\x1btypedslicec" +
"end\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x01\x00\x00\t\x17selectnbrecv" + "opy\x00\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selec" +
"\x00\x06\x17\"\xf2\x01\x00\x00>p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19selectnbr" + "tnbsend\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x01\x00\x00\t\x17selectn" +
"ecv2\x00\b\x17\"\xf2\x01\x00\x00>p\x00\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhcha" + "brecv\x00\x06\x17\"\xf2\x01\x00\x00\x17:p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19sel" +
"n·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsi" + "ectnbrecv2\x00\b\x17\"\xf2\x01\x00\x00\x17:p\x00\x00\x17\x00\x15received·4\x00\x00\x1f" +
"ze·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel\xc2" + "\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00" +
"\xb72\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13select" + "\n\x13selsize·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06" +
"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" + "\x17\"\vsel·2\x00\x00\x1f\x04:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00" +
"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" + "\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" +
"\x00\xb8\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb6\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t\x0fsele" + "\x15selectrecv2\x00\b\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x17\x00\x15rece" +
"ctgo\x00\x02\x17\"\xae\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\b\x00" + "ived·5\x00\x00\x02\x00\xb8\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb6\x02\x00\x00\x02\x00" +
"\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x11grows" + "\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\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:\xcc\x02\x00\x00\t\rmemm" + "lice\x00\x06\x17\"\b\x00\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·" +
"ove\x00\x06>\tto·1\x00\x00>\vfrm·2\x00\x00\x16\x11length·3\x00d\x00\t\v" + "1\x00\x00\t\x11growslice\x00\x06\x17\"\b\x00\x00\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:" +
"memclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length·2\x00d\x00\t\x0fmemeq" + "\xcc\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00\x17:\vfrm·2\x00\x00\x16\x11le" +
"ual\x00\x06>\ax·2\x00\x00>\ay·3\x00\x00\x16\rsize·4\x00d\x01\x00\x00\t\x11mem" + "ngth·3\x00d\x00\t\vmemclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length\xc2" +
"equal8\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04>\xe2\x02\x00\x00" + "\xb72\x00d\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00\x00\x17:\ay·3\x00\x00\x16\rsiz" +
">\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13mem" + "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" +
"equal64\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04>\xe2\x02" + "emequal16\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04" +
"\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div" + "\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\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00" + "\x00\x01\x00\x00\t\x15memequal128\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x0fint6" +
"\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64" + "4div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64" +
"touint64\x00\x01\x1a\x00\x01\x14\x00\t\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1d" + "mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat6" +
"uint64tofloat64\x00\x01\x14\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e" + "4toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64touint64\x00\x01\x1a\x00\x01\x14\x00\t" +
"\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefunc" + "\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00" +
"enter\x00\x01\x16d\x00\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01\x16" + "\x01\x14\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e\vnum·2\x00\x00\x1e\vden·" +
"d\x00\t\x11racewrite\x00\x01\x16d\x00\t\x19racereadrange\x00\x04\x16\radd" + "3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefuncenter\x00\x01\x16d\x00\t\x17race" +
"r·1\x00d\x16\rsize·2\x00d\x00\t\x1bracewriterange\x00\x04\x16\x94\x03\x00" + "funcexit\x00\x00\x00\t\x0fraceread\x00\x01\x16d\x00\t\x11racewrite\x00\x01\x16" +
"d\x16\x96\x03\x00d\x00\t\x0fmsanread\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x11msanwrit" + "d\x00\t\x19racereadrange\x00\x04\x16\raddr·1\x00d\x16\rsize·2\x00" +
"e\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\v\xf4\x01\x02\v\x00\x01\x00\n$$\n" "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 = "" + const unsafeimport = "" +
"c\x00\x03v0\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01:" + "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" + ":\x00\x01\x16\x00\t\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\b\x00\v" +
"\x01\x00\n$$\n" "\x00\x01\x00\n$$\n"

View file

@ -518,8 +518,7 @@ func cgen_wb(n, res *Node, wb bool) {
case ODOT, case ODOT,
ODOTPTR, ODOTPTR,
OINDEX, OINDEX,
OIND, OIND:
ONAME: // PHEAP or PPARAMREF var
var n1 Node var n1 Node
Igen(n, &n1, res) Igen(n, &n1, res)
@ -1545,6 +1544,7 @@ func Agen(n *Node, res *Node) {
switch n.Op { switch n.Op {
default: default:
Dump("bad agen", n)
Fatalf("agen: unknown op %v", Nconv(n, FmtShort|FmtSign)) Fatalf("agen: unknown op %v", Nconv(n, FmtShort|FmtSign))
case OCALLMETH: case OCALLMETH:
@ -1571,24 +1571,6 @@ func Agen(n *Node, res *Node) {
Thearch.Gmove(&n1, res) Thearch.Gmove(&n1, res)
Regfree(&n1) 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: case OIND:
Cgen(nl, res) Cgen(nl, res)
if !nl.NonNil { if !nl.NonNil {
@ -1646,8 +1628,9 @@ func Igen(n *Node, a *Node, res *Node) {
switch n.Op { switch n.Op {
case ONAME: case ONAME:
if (n.Class&PHEAP != 0) || n.Class == PPARAMREF { if n.Class == PAUTOHEAP {
break Dump("igen", n)
Fatalf("bad name")
} }
*a = *n *a = *n
return return
@ -1702,11 +1685,11 @@ func Igen(n *Node, a *Node, res *Node) {
a.Type = n.Type a.Type = n.Type
return return
// 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: case OINDEX:
// Index of fixed-size array by constant can
// put the offset in the addressing.
// Could do the same for slice except that we need
// to use the real index for the bounds checking.
if n.Left.Type.IsArray() || (n.Left.Type.IsPtr() && n.Left.Left.Type.IsArray()) { if n.Left.Type.IsArray() || (n.Left.Type.IsPtr() && n.Left.Left.Type.IsArray()) {
if Isconst(n.Right, CTINT) { if Isconst(n.Right, CTINT) {
// Compute &a. // Compute &a.

View file

@ -66,8 +66,39 @@ func closurebody(body []*Node) *Node {
// unhook them. // unhook them.
// make the list of pointers for the closure call. // make the list of pointers for the closure call.
for _, v := range func_.Func.Cvars.Slice() { for _, v := range func_.Func.Cvars.Slice() {
v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer // Unlink from v1; see comment in syntax.go type Param for these fields.
v.Name.Param.Outerexpr = oldname(v.Sym) 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_ return func_
@ -75,7 +106,7 @@ func closurebody(body []*Node) *Node {
func typecheckclosure(func_ *Node, top int) { func typecheckclosure(func_ *Node, top int) {
for _, ln := range func_.Func.Cvars.Slice() { for _, ln := range func_.Func.Cvars.Slice() {
n := ln.Name.Param.Closure n := ln.Name.Defn
if !n.Name.Captured { if !n.Name.Captured {
n.Name.Captured = true n.Name.Captured = true
if n.Name.Decldepth == 0 { 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 // We use value capturing for values <= 128 bytes that are never reassigned
// after capturing (effectively constant). // after capturing (effectively constant).
func capturevars(xfunc *Node) { func capturevars(xfunc *Node) {
var outer *Node
lno := lineno lno := lineno
lineno = xfunc.Lineno lineno = xfunc.Lineno
@ -239,14 +268,14 @@ func capturevars(xfunc *Node) {
// so that the outer frame also grabs them and knows they escape. // so that the outer frame also grabs them and knows they escape.
dowidth(v.Type) dowidth(v.Type)
outer = v.Name.Param.Outerexpr outer := v.Name.Param.Outer
v.Name.Param.Outerexpr = nil outermost := v.Name.Defn
// out parameters will be assigned to implicitly upon return. // 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 v.Name.Byval = true
} else { } else {
v.Name.Param.Closure.Addrtaken = true outermost.Addrtaken = true
outer = Nod(OADDR, outer, nil) outer = Nod(OADDR, outer, nil)
} }
@ -259,7 +288,7 @@ func capturevars(xfunc *Node) {
if v.Name.Byval { if v.Name.Byval {
how = "value" 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) outer = typecheck(outer, Erv)
@ -303,7 +332,7 @@ func transformclosure(xfunc *Node) {
continue continue
} }
fld := newField() fld := newField()
fld.Funarg = true fld.Funarg = FunargParams
if v.Name.Byval { if v.Name.Byval {
// If v is captured by value, we merely downgrade it to PPARAM. // If v is captured by value, we merely downgrade it to PPARAM.
v.Class = PPARAM v.Class = PPARAM
@ -313,7 +342,7 @@ func transformclosure(xfunc *Node) {
} else { } else {
// If v of type T is captured by reference, // If v of type T is captured by reference,
// we introduce function param &v *T // 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). // (accesses will implicitly deref &v).
addr := newname(Lookupf("&%s", v.Sym.Name)) addr := newname(Lookupf("&%s", v.Sym.Name))
addr.Type = Ptrto(v.Type) 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, ODOTPTR,
OINDEX, OINDEX,
OIND, OIND,
ONAME, // PHEAP or PPARAMREF var
OCALLFUNC, OCALLFUNC,
OCALLMETH, OCALLMETH,
OCALLINTER: 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 { 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 // TODO(rsc): If there is an outer variable x and we
// are parsing x := 5 inside the closure, until we get to // are parsing x := 5 inside the closure, until we get to
// the := it looks like a reference to the outer x so we'll // the := it looks like a reference to the outer x so we'll
// make x a closure variable unnecessarily. // make x a closure variable unnecessarily.
if n.Name.Param.Closure == nil || n.Name.Param.Closure.Name.Funcdepth != Funcdepth { c := n.Name.Param.Innermost
// create new closure var. if c == nil || c.Name.Funcdepth != Funcdepth {
c := Nod(ONAME, nil, nil) // Do not have a closure var for the active closure yet; make one.
c = Nod(ONAME, nil, nil)
c.Sym = s c.Sym = s
c.Class = PPARAMREF c.Class = PAUTOHEAP
c.setIsClosureVar(true)
c.Isddd = n.Isddd c.Isddd = n.Isddd
c.Name.Defn = n c.Name.Defn = n
c.Addable = false c.Addable = false
c.Ullman = 2 c.Ullman = 2
c.Name.Funcdepth = Funcdepth c.Name.Funcdepth = Funcdepth
c.Name.Param.Outer = n.Name.Param.Closure
n.Name.Param.Closure = c // Link into list of active closure variables.
c.Name.Param.Closure = n // Popped from list in func closurebody.
c.Name.Param.Outer = n.Name.Param.Innermost
n.Name.Param.Innermost = c
c.Xoffset = 0 c.Xoffset = 0
Curfn.Func.Cvars.Append(c) Curfn.Func.Cvars.Append(c)
} }
// return ref to closure var, not original // return ref to closure var, not original
return n.Name.Param.Closure return c
} }
return n return n
@ -504,10 +508,8 @@ func ifacedcl(n *Node) {
n.Func = new(Func) n.Func = new(Func)
n.Func.FCurfn = Curfn n.Func.FCurfn = Curfn
dclcontext = PPARAM dclcontext = PPARAM
markdcl()
Funcdepth++ funcstart(n)
n.Func.Outer = Curfn
Curfn = n
funcargs(n.Right) funcargs(n.Right)
// funcbody is normally called after the parser has // funcbody is normally called after the parser has
@ -534,11 +536,7 @@ func funchdr(n *Node) {
} }
dclcontext = PAUTO dclcontext = PAUTO
markdcl() funcstart(n)
Funcdepth++
n.Func.Outer = Curfn
Curfn = n
if n.Func.Nname != nil { if n.Func.Nname != nil {
funcargs(n.Func.Nname.Name.Param.Ntype) 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. // finish the body.
// called in auto-declaration context. // called in auto-declaration context.
// returns in extern-declaration context. // returns in extern-declaration context.
@ -680,9 +690,8 @@ func funcbody(n *Node) {
Fatalf("funcbody: unexpected dclcontext %d", dclcontext) Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
} }
popdcl() popdcl()
funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1]
Funcdepth-- Funcdepth--
Curfn = n.Func.Outer
n.Func.Outer = nil
if Funcdepth == 0 { if Funcdepth == 0 {
dclcontext = PEXTERN 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 := typ(TSTRUCT)
t.StructType().Funarg = true t.StructType().Funarg = funarg
fields := make([]*Field, len(l)) fields := make([]*Field, len(l))
for i, n := range l { for i, n := range l {
f := structfield(n) f := structfield(n)
f.Funarg = true f.Funarg = funarg
// esc.go needs to find f given a PPARAM to add the tag. // esc.go needs to find f given a PPARAM to add the tag.
if n.Left != nil && n.Left.Class == PPARAM { if n.Left != nil && n.Left.Class == PPARAM {
@ -1025,9 +1034,9 @@ func functype0(t *Type, this *Node, in, out []*Node) {
if this != nil { if this != nil {
rcvr = []*Node{this} rcvr = []*Node{this}
} }
*t.RecvsP() = tofunargs(rcvr) *t.RecvsP() = tofunargs(rcvr, FunargRcvr)
*t.ResultsP() = tofunargs(out) *t.ResultsP() = tofunargs(out, FunargResults)
*t.ParamsP() = tofunargs(in) *t.ParamsP() = tofunargs(in, FunargParams)
checkdupfields("argument", t.Recvs(), t.Results(), t.Params()) 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 // "Big" conditions that were scattered around in walk have been gathered here
if n.Esc != EscHeap && n.Type != nil && if n.Esc != EscHeap && n.Type != nil &&
(n.Type.Width > MaxStackVarSize || (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)) { n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
if Debug['m'] > 2 { if Debug['m'] > 2 {
Warnl(n.Lineno, "%v is too large for stack", n) 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") escassignSinkNilWhy(e, n, n7.Right, "map literal value")
} }
// Link addresses of captured variables to closure.
case OCLOSURE: case OCLOSURE:
// Link addresses of captured variables to closure.
for _, v := range n.Func.Cvars.Slice() { for _, v := range n.Func.Cvars.Slice() {
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
continue continue
} }
a := v.Name.Param.Closure a := v.Name.Defn
if !v.Name.Byval { if !v.Name.Byval {
a = Nod(OADDR, a, nil) a = Nod(OADDR, a, nil)
a.Lineno = v.Lineno a.Lineno = v.Lineno
@ -1068,7 +1068,6 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
OIND, // dst = *x OIND, // dst = *x
ODOTPTR, // dst = (*x).f ODOTPTR, // dst = (*x).f
ONAME, ONAME,
OPARAM,
ODDDARG, ODDDARG,
OPTRLIT, OPTRLIT,
OARRAYLIT, 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. // original variable.
if src.Class == PPARAMREF { if src.isClosureVar() {
if leaks && Debug['m'] != 0 { if leaks && Debug['m'] != 0 {
Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort)) Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort))
step.describe(src) 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: case OPTRLIT, OADDR:
@ -1835,20 +1834,20 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
} }
if leaks { if leaks {
src.Esc = EscHeap src.Esc = EscHeap
addrescapes(src.Left)
if Debug['m'] != 0 && osrcesc != src.Esc { if Debug['m'] != 0 && osrcesc != src.Esc {
p := src p := src
if p.Left.Op == OCLOSURE { if p.Left.Op == OCLOSURE {
p = p.Left // merely to satisfy error messages in tests p = p.Left // merely to satisfy error messages in tests
} }
if Debug['m'] > 2 { if Debug['m'] > 2 {
Warnl(src.Lineno, "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v", Warnl(src.Lineno, "%v escapes to heap, level=%v, dst=%v dst.eld=%v, src.eld=%v",
Nconv(p, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth) Nconv(p, FmtShort), level, dst, dstE.Escloopdepth, modSrcLoopdepth)
} else { } else {
Warnl(src.Lineno, "%v escapes to heap", Nconv(p, FmtShort)) Warnl(src.Lineno, "%v escapes to heap", Nconv(p, FmtShort))
step.describe(src) step.describe(src)
} }
} }
addrescapes(src.Left)
escwalkBody(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth) 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 extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op
} else { } else {

View file

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

View file

@ -218,9 +218,9 @@ var classnames = []string{
"Pxxx", "Pxxx",
"PEXTERN", "PEXTERN",
"PAUTO", "PAUTO",
"PAUTOHEAP",
"PPARAM", "PPARAM",
"PPARAMOUT", "PPARAMOUT",
"PPARAMREF",
"PFUNC", "PFUNC",
} }
@ -251,14 +251,10 @@ func jconv(n *Node, flag FmtFlag) string {
} }
if n.Class != 0 { if n.Class != 0 {
s := "" if int(n.Class) < len(classnames) {
if n.Class&PHEAP != 0 { fmt.Fprintf(&buf, " class(%s)", classnames[n.Class])
s = ",heap"
}
if int(n.Class&^PHEAP) < len(classnames) {
fmt.Fprintf(&buf, " class(%s%s)", classnames[n.Class&^PHEAP], s)
} else { } 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 { switch n.Op {
case ODCL: case ODCL:
if fmtmode == FExp { if fmtmode == FExp {
switch n.Left.Class &^ PHEAP { switch n.Left.Class {
case PPARAM, PPARAMOUT, PAUTO: case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP:
f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type) f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type)
goto ret goto ret
} }
@ -1197,7 +1193,7 @@ func exprfmt(n *Node, prec int) string {
if n.Nbody.Len() != 0 { if n.Nbody.Len() != 0 {
return fmt.Sprintf("%v { %v }", n.Type, n.Nbody) 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: case OCOMPLIT:
ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && n.Right.Type.IsPtr() 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 s != nil && f.Embedded == 0 {
if f.Funarg { if f.Funarg != FunargNone {
name = Nconv(f.Nname, 0) name = Nconv(f.Nname, 0)
} else if flag&FmtLong != 0 { } else if flag&FmtLong != 0 {
name = sconv(s, FmtShort|FmtByte) // qualify non-exported names (used on structs, not on funarg) 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.) // (The escape analysis tags do not apply to func vars.)
// But it must not suppress struct field tags. // But it must not suppress struct field tags.
// See golang.org/issue/13777 and golang.org/issue/14331. // 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) str += " " + strconv.Quote(f.Note)
} }

View file

@ -43,53 +43,39 @@ func addrescapes(n *Node) {
break break
} }
switch n.Class { // If a closure reference escapes, mark the outer variable as escaping.
case PPARAMREF: if n.isClosureVar() {
addrescapes(n.Name.Defn) addrescapes(n.Name.Defn)
break
// 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")
}
n.Name.Param.Stackparam.Xoffset = n.Xoffset
fallthrough
case PAUTO:
n.Class |= PHEAP
n.Addable = false
n.Ullman = 2
n.Xoffset = 0
// create stack variable to hold pointer to heap
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)
}
Curfn = oldfn
} }
if n.Class != PPARAM && n.Class != PPARAMOUT && n.Class != PAUTO {
break
}
// This is a plain parameter or local variable that needs to move to the heap,
// but possibly for the function outside the one we're compiling.
// That is, if we have:
//
// func f(x int) {
// func() {
// global = &x
// }
// }
//
// then we're analyzing the inner closure but we need to move x to the
// heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
oldfn := Curfn
Curfn = n.Name.Curfn
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
Curfn = Curfn.Func.Closure
}
ln := lineno
lineno = Curfn.Lineno
moveToHeap(n)
Curfn = oldfn
lineno = ln
case OIND, ODOTPTR: case OIND, ODOTPTR:
break 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() { func clearlabels() {
for _, l := range labellist { for _, l := range labellist {
l.Sym.Label = nil l.Sym.Label = nil
@ -243,16 +333,9 @@ func cgen_dcl(n *Node) {
Fatalf("cgen_dcl") Fatalf("cgen_dcl")
} }
if n.Class&PHEAP == 0 { if n.Class == PAUTOHEAP {
return 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 // generate discard of value
@ -263,7 +346,7 @@ func cgen_discard(nr *Node) {
switch nr.Op { switch nr.Op {
case ONAME: 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) gused(nr)
} }
@ -908,11 +991,6 @@ func Cgen_as_wb(nl, nr *Node, wb bool) {
} }
if nr == nil || iszero(nr) { if nr == nil || iszero(nr) {
// heaps should already be clear
if nr == nil && (nl.Class&PHEAP != 0) {
return
}
tl := nl.Type tl := nl.Type
if tl == nil { if tl == nil {
return return

View file

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

View file

@ -53,7 +53,6 @@ func Ismem(n *Node) bool {
OCAP, OCAP,
OINDREG, OINDREG,
ONAME, ONAME,
OPARAM,
OCLOSUREVAR: OCLOSUREVAR:
return true return true
@ -349,18 +348,6 @@ func Naddr(a *obj.Addr, n *Node) {
a.Width = 0 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: case OCLOSUREVAR:
if !Curfn.Func.Needctxt { if !Curfn.Func.Needctxt {
Fatalf("closurevar without needctxt") Fatalf("closurevar without needctxt")
@ -528,25 +515,36 @@ func newplist() *obj.Plist {
return pl return pl
} }
// nodarg does something that depends on the value of // nodarg returns a Node for the function argument denoted by t,
// fp (this was previously completely undocumented). // 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 // If fp is 0, the node is for use by a caller invoking the given
// fp=0 corresponds to output args // function, preparing the arguments before the call
// fp=-1 is a special case of output args for a // or retrieving the results after the call.
// specific call from walk that previously (and // In this case, the node will correspond to an outgoing argument
// incorrectly) passed a 1; the behavior is exactly // slot like 8(SP).
// the same as it is for 1, except that PARAMOUT is //
// generated instead of PARAM. // 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 { func nodarg(t interface{}, fp int) *Node {
var n *Node var n *Node
var funarg Funarg
switch t := t.(type) { switch t := t.(type) {
default:
Fatalf("bad nodarg %T(%v)", t, t)
case *Type: case *Type:
// entire argument struct, not just one arg // Entire argument struct, not just one arg
if !t.IsFuncArgStruct() { if !t.IsFuncArgStruct() {
Fatalf("nodarg: bad type %v", t) Fatalf("nodarg: bad type %v", t)
} }
funarg = t.StructType().Funarg
// Build fake variable name for whole arg struct.
n = Nod(ONAME, nil, nil) n = Nod(ONAME, nil, nil)
n.Sym = Lookup(".args") n.Sym = Lookup(".args")
n.Type = t n.Type = t
@ -559,15 +557,43 @@ func nodarg(t interface{}, fp int) *Node {
} }
n.Xoffset = first.Offset n.Xoffset = first.Offset
n.Addable = true n.Addable = true
case *Field: 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 { for _, n := range Curfn.Func.Dcl {
if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { 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 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 = Nod(ONAME, nil, nil)
n.Type = t.Type n.Type = t.Type
n.Sym = t.Sym n.Sym = t.Sym
@ -577,8 +603,6 @@ func nodarg(t interface{}, fp int) *Node {
n.Xoffset = t.Offset n.Xoffset = t.Offset
n.Addable = true n.Addable = true
n.Orig = t.Nname n.Orig = t.Nname
default:
panic("unreachable")
} }
// Rewrite argument named _ to __, // Rewrite argument named _ to __,
@ -589,23 +613,23 @@ func nodarg(t interface{}, fp int) *Node {
} }
switch fp { switch fp {
case 0: // output arg default:
n.Op = OINDREG Fatalf("bad fp")
case 0: // preparing arguments for call
n.Op = OINDREG
n.Reg = int16(Thearch.REGSP) n.Reg = int16(Thearch.REGSP)
n.Xoffset += Ctxt.FixedFrameSize() n.Xoffset += Ctxt.FixedFrameSize()
case 1: // input arg case 1: // reading arguments inside call
n.Class = PPARAM n.Class = PPARAM
if funarg == FunargResults {
case -1: // output arg from paramstoheap n.Class = PPARAMOUT
n.Class = PPARAMOUT }
case 2: // offset output arg
Fatalf("shouldn't be used")
} }
n.Typecheck = 1 n.Typecheck = 1
n.Addrtaken = true // keep optimizers at bay
return n return n
} }

View file

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

View file

@ -27,9 +27,7 @@
package gc package gc
import ( import "fmt"
"fmt"
)
// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods // 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. // 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 *budget -= fn.InlCost
break break
} }
if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions if n.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 { if d := n.Left.Sym.Def; d != nil && d.Func.Inl.Len() != 0 {
*budget -= d.Func.InlCost *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. if ln.Class == PPARAMOUT { // return values handled below.
continue continue
} }
if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
continue
}
if ln.Op == ONAME { if ln.Op == ONAME {
ln.Name.Inlvar = inlvar(ln) ln.Name.Inlvar = typecheck(inlvar(ln), Erv)
if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM {
// Typecheck because inlvar is not necessarily a function parameter. ninit.Append(Nod(ODCL, ln.Name.Inlvar, nil))
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
} }
} }
} }

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`") flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
obj.Flagcount("l", "disable inlining", &Debug['l']) 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("live", "debug liveness analysis", &debuglive)
obj.Flagcount("m", "print optimization decisions", &Debug['m']) obj.Flagcount("m", "print optimization decisions", &Debug['m'])
flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer") 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 p != "empty archive" {
if !strings.HasPrefix(p, "go object ") { 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() 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) // assume files move (get installed)
// so don't record the full path. // so don't record the full path.
linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib 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 // Run this after changing builtin/runtime.go and builtin/unsafe.go
// or after changing the export metadata format in the compiler. // or after changing the export metadata format in the compiler.
// Either way, you need to have a working compiler binary first. // 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 package main
import ( 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)) 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() { func dumpobj() {
if linkobj == "" {
dumpobj1(outfile, modeCompilerObj|modeLinkerObj)
} else {
dumpobj1(outfile, modeCompilerObj)
dumpobj1(linkobj, modeLinkerObj)
}
}
func dumpobj1(outfile string, mode int) {
var err error var err error
bout, err = bio.Create(outfile) bout, err = bio.Create(outfile)
if err != nil { if err != nil {
@ -40,8 +67,27 @@ func dumpobj() {
startobj = bout.Offset() startobj = bout.Offset()
} }
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) printheader := func() {
dumpexport() 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 { if writearchive {
bout.Flush() bout.Flush()
@ -53,12 +99,20 @@ func dumpobj() {
formathdr(arhdr[:], "__.PKGDEF", size) formathdr(arhdr[:], "__.PKGDEF", size)
bout.Write(arhdr[:]) bout.Write(arhdr[:])
bout.Flush() bout.Flush()
bout.Seek(startobj+size+(size&1), 0) bout.Seek(startobj+size+(size&1), 0)
}
if mode&modeLinkerObj == 0 {
bout.Close()
return
}
if writearchive {
// start object file
arhdr = [ArhdrSize]byte{} arhdr = [ArhdrSize]byte{}
bout.Write(arhdr[:]) bout.Write(arhdr[:])
startobj = bout.Offset() startobj = bout.Offset()
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) printheader()
} }
if pragcgobuf != "" { if pragcgobuf != "" {

View file

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

View file

@ -1082,6 +1082,20 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
n.Left = orderaddrtemp(n.Left, order) 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: case OANDAND, OOROR:
mark := marktemp(order) mark := marktemp(order)
n.Left = orderexpr(n.Left, order, nil) n.Left = orderexpr(n.Left, order, nil)

View file

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

View file

@ -197,54 +197,41 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool {
return false return false
} }
// Collects and returns a slice of *Nodes for functions arguments and local // livenessShouldTrack reports whether the liveness analysis
// variables. // 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 { func getvariables(fn *Node) []*Node {
var result []*Node var vars []*Node
for _, ln := range fn.Func.Dcl { for _, n := range fn.Func.Dcl {
if ln.Op == ONAME { if n.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.
//
// The Node.opt field is available for use by optimization passes. // 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 // We use it to hold the index of the node in the variables array
// (so that 0 means the Node is not in the variables array). // (nil 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.
// The Node.curfn field is supposed to be set to the current function // 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, // already, but for some compiler-introduced names it seems not to be,
// so fix that here. // so fix that here.
// Later, when we want to find the index of a node in the variables list, // 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. // is the index in the variables list.
ln.SetOpt(nil) n.SetOpt(nil)
n.Name.Curfn = Curfn
}
// The compiler doesn't emit initializations for zero-width parameters or results. if livenessShouldTrack(n) {
if ln.Type.Width == 0 { n.SetOpt(int32(len(vars)))
continue vars = append(vars, n)
}
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)
}
} }
} }
return result return vars
} }
// A pretty printer for control flow graphs. Takes a slice of *BasicBlocks. // 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 // read the out arguments - they won't be set until the new
// function runs. // function runs.
for i, node := range vars { for i, node := range vars {
switch node.Class &^ PHEAP { switch node.Class {
case PPARAM: case PPARAM:
bvset(uevar, int32(i)) if !node.NotLiveAtEnd() {
bvset(uevar, int32(i))
}
// If the result had its address taken, it is being tracked // If the result had its address taken, it is being tracked
// by the avarinit code, which does not use uevar. // 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 // A text instruction marks the entry point to a function and
// the definition point of all in arguments. // the definition point of all in arguments.
for i, node := range vars { for i, node := range vars {
switch node.Class &^ PHEAP { switch node.Class {
case PPARAM: case PPARAM:
if node.Addrtaken { if node.Addrtaken {
bvset(avarinit, int32(i)) 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 { if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
from := &prog.From from := &prog.From
if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Name.Curfn == Curfn { if from.Node != nil && from.Sym != nil {
switch ((from.Node).(*Node)).Class &^ PHEAP { n := from.Node.(*Node)
case PAUTO, PPARAM, PPARAMOUT: if pos := liveIndex(n, vars); pos >= 0 {
pos, ok := from.Node.(*Node).Opt().(int32) // index in vars if n.Addrtaken {
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 {
bvset(avarinit, pos) bvset(avarinit, pos)
} else { } else {
if prog.Info.Flags&(LeftRead|LeftAddr) != 0 { if prog.Info.Flags&(LeftRead|LeftAddr) != 0 {
bvset(uevar, pos) bvset(uevar, pos)
} }
if prog.Info.Flags&LeftWrite != 0 { if prog.Info.Flags&LeftWrite != 0 {
if from.Node != nil && !Isfat(((from.Node).(*Node)).Type) { if !Isfat(n.Type) {
bvset(varkill, pos) 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 { if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
to := &prog.To to := &prog.To
if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Name.Curfn == Curfn { if to.Node != nil && to.Sym != nil {
switch ((to.Node).(*Node)).Class &^ PHEAP { n := to.Node.(*Node)
case PAUTO, PPARAM, PPARAMOUT: if pos := liveIndex(n, vars); pos >= 0 {
pos, ok := to.Node.(*Node).Opt().(int32) // index in vars if n.Addrtaken {
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 prog.As != obj.AVARKILL { if prog.As != obj.AVARKILL {
bvset(avarinit, pos) bvset(avarinit, pos)
} }
@ -665,7 +640,7 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
bvset(uevar, pos) bvset(uevar, pos)
} }
if prog.Info.Flags&RightWrite != 0 { 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) 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 // 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 // liveness computation. The cfg argument is a slice of *BasicBlocks and the
// vars argument is a slice of *Nodes. // vars argument is a slice of *Nodes.
@ -812,8 +805,7 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) {
return return
} }
for _, a := range fn.Func.Dcl { for _, a := range fn.Func.Dcl {
class := a.Class &^ PHEAP if a.Op == ONAME && (a.Class == PPARAM || a.Class == PPARAMOUT) && a == n {
if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n {
return return
} }
} }
@ -980,23 +972,6 @@ func onebitlivepointermap(lv *Liveness, liveout bvec, vars []*Node, args bvec, l
onebitwalktype1(node.Type, &xoffset, args) 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. // 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 case OPRINT, // don't bother instrumenting it
OPRINTN, // don't bother instrumenting it OPRINTN, // don't bother instrumenting it
OCHECKNIL, // always followed by a read. OCHECKNIL, // always followed by a read.
OPARAM, // it appears only in fn->exit to copy heap params back
OCLOSUREVAR, // immutable pointer to captured variable OCLOSUREVAR, // immutable pointer to captured variable
ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT)
OINDREG, // at this stage, only n(SP) nodes from nodarg 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 // e.g. if we've got a local variable/method receiver
// that has got a pointer inside. Whether it points to // that has got a pointer inside. Whether it points to
// the heap or not is impossible to know at compile time // 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 hascalls := 0
foreach(n, hascallspred, &hascalls) foreach(n, hascallspred, &hascalls)
if hascalls != 0 { if hascalls != 0 {

View file

@ -516,7 +516,7 @@ func isliteral(n *Node) bool {
} }
func (n *Node) isSimpleName() 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) { 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 _64bit uintptr // size on 64bit platforms
}{ }{
{Flow{}, 52, 88}, {Flow{}, 52, 88},
{Func{}, 96, 168}, {Func{}, 92, 160},
{Name{}, 52, 80}, {Name{}, 52, 80},
{Node{}, 92, 144}, {Node{}, 92, 144},
{Sym{}, 60, 112}, {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. // the function.
s.returns = append(s.returns, n) s.returns = append(s.returns, n)
} }
case PAUTO | PHEAP: if n.Class == PPARAM && s.canSSA(n) && n.Type.IsPtrShaped() {
// TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition s.ptrargs = append(s.ptrargs, n)
aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n}) n.SetNotLiveAtEnd(true) // SSA takes care of this explicitly
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.
case PAUTO: case PAUTO:
// processed at each use, to prevent Addr coming // processed at each use, to prevent Addr coming
// before the decl. // before the decl.
case PAUTOHEAP:
// moved to heap - already handled by frontend
case PFUNC: case PFUNC:
// local function - already handled by frontend // local function - already handled by frontend
default: default:
str := "" s.Unimplementedf("local variable with class %s unimplemented", classnames[n.Class])
if n.Class&PHEAP != 0 {
str = ",heap"
}
s.Unimplementedf("local variable with class %s%s unimplemented", classnames[n.Class&^PHEAP], str)
} }
} }
@ -218,8 +214,16 @@ func buildssa(fn *Node) *ssa.Func {
return nil return nil
} }
prelinkNumvars := s.f.NumValues()
sparseDefState := s.locatePotentialPhiFunctions(fn)
// Link up variable uses to variable definitions // 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 // Don't carry reference this around longer than necessary
s.exitCode = Nodes{} s.exitCode = Nodes{}
@ -282,9 +286,13 @@ type state struct {
// list of FwdRef values. // list of FwdRef values.
fwdRefs []*ssa.Value fwdRefs []*ssa.Value
// list of PPARAMOUT (return) variables. Does not include PPARAM|PHEAP vars. // list of PPARAMOUT (return) variables.
returns []*Node 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 cgoUnsafeArgs bool
noWB bool noWB bool
WBLineno int32 // line number of first write barrier. 0=no write barriers WBLineno int32 // line number of first write barrier. 0=no write barriers
@ -577,24 +585,9 @@ func (s *state) stmt(n *Node) {
return return
case ODCL: case ODCL:
if n.Left.Class&PHEAP == 0 { if n.Left.Class == PAUTOHEAP {
return 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: case OLABEL:
sym := n.Left.Sym sym := n.Left.Sym
@ -980,8 +973,7 @@ func (s *state) exit() *ssa.Block {
// Store SSAable PPARAMOUT variables back to stack locations. // Store SSAable PPARAMOUT variables back to stack locations.
for _, n := range s.returns { for _, n := range s.returns {
aux := &ssa.ArgSymbol{Typ: n.Type, Node: n} addr := s.decladdrs[n]
addr := s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
val := s.variable(n, n.Type) val := s.variable(n, n.Type)
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, n, s.mem()) 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()) 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. // 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. // Do actual return.
m := s.mem() m := s.mem()
b := s.endBlock() b := s.endBlock()
@ -1428,9 +1430,6 @@ func (s *state) expr(n *Node) *ssa.Value {
case OCFUNC: case OCFUNC:
aux := s.lookupSymbol(n, &ssa.ExternSymbol{Typ: n.Type, Sym: n.Left.Sym}) aux := s.lookupSymbol(n, &ssa.ExternSymbol{Typ: n.Type, Sym: n.Left.Sym})
return s.entryNewValue1A(ssa.OpAddr, n.Type, aux, s.sb) 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: case ONAME:
if n.Class == PFUNC { if n.Class == PFUNC {
// "value" of a function is the address of the function's closure // "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. // Start exit block, find address of result.
s.startBlock(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())
}
res := n.Left.Type.Results() res := n.Left.Type.Results()
if res.NumFields() == 0 || k != callNormal { if res.NumFields() == 0 || k != callNormal {
// call has no return value. Continue with the next statement. // 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 // that cse works on their addresses
aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n}) aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
return s.newValue1A(ssa.OpAddr, t, aux, s.sp) return s.newValue1A(ssa.OpAddr, t, aux, s.sp)
case PAUTO | PHEAP, PPARAM | PHEAP, PPARAMOUT | PHEAP, PPARAMREF:
return s.expr(n.Name.Heapaddr)
default: default:
s.Unimplementedf("variable address class %v not implemented", n.Class) s.Unimplementedf("variable address class %v not implemented", classnames[n.Class])
return nil return nil
} }
case OINDREG: case OINDREG:
@ -2770,17 +2771,6 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
case OCLOSUREVAR: case OCLOSUREVAR:
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
s.entryNewValue0(ssa.OpGetClosurePtr, Ptrto(Types[TUINT8]))) 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: case OCONVNOP:
addr := s.addr(n.Left, bounded) addr := s.addr(n.Left, bounded)
return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type 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 { if n.Addrtaken {
return false return false
} }
if n.Class&PHEAP != 0 { if n.isParamHeapCopy() {
return false return false
} }
if n.Class == PAUTOHEAP {
Fatalf("canSSA of PAUTOHEAP %v", n)
}
switch n.Class { switch n.Class {
case PEXTERN, PPARAMREF: case PEXTERN:
// TODO: maybe treat PPARAMREF with an Arg-like op to read from closure?
return false return false
case PPARAMOUT: case PPARAMOUT:
if hasdefer { if hasdefer {
@ -2993,6 +2985,11 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val
b.AddEdgeTo(bNext) b.AddEdgeTo(bNext)
s.startBlock(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 // Load results
res := make([]*ssa.Value, len(results)) res := make([]*ssa.Value, len(results))
for i, t := range results { for i, t := range results {
@ -3743,7 +3740,8 @@ func (s *state) mem() *ssa.Value {
return s.variable(&memVar, ssa.TypeMem) 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 // Build SSA graph. Each variable on its first use in a basic block
// leaves a FwdRef in that block representing the incoming value // leaves a FwdRef in that block representing the incoming value
// of that variable. This function links that ref up with possible definitions, // 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 { for len(s.fwdRefs) > 0 {
v := s.fwdRefs[len(s.fwdRefs)-1] v := s.fwdRefs[len(s.fwdRefs)-1]
s.fwdRefs = 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. // resolveFwdRef modifies v to be the variable's value at the start of its block.
// v must be a FwdRef op. // 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 b := v.Block
name := v.Aux.(*Node) name := v.Aux.(*Node)
v.Aux = nil v.Aux = nil
@ -3803,6 +3801,7 @@ func (s *state) resolveFwdRef(v *ssa.Value) {
args := argstore[:0] args := argstore[:0]
for _, e := range b.Preds { for _, e := range b.Preds {
p := e.Block() p := e.Block()
p = dm.FindBetterDefiningBlock(name, p) // try sparse improvement on p
args = append(args, s.lookupVarOutgoing(p, v.Type, name, v.Line)) 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. // TestFP tests that both backends have the same result for floating point expressions.
func TestFP(t *testing.T) { func TestFP(t *testing.T) { runTest(t, "fp_ssa.go") }
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")
}
// TestArithmeticBoundary tests boundary results for arithmetic operations. // TestArithmeticBoundary tests boundary results for arithmetic operations.
func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary_ssa.go") } func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary_ssa.go") }

View file

@ -87,8 +87,39 @@ func linestr(line int32) string {
return Ctxt.Line(int(line)) 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{}) { 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() hcrash()
nerrors++ nerrors++
@ -99,32 +130,8 @@ func yyerrorl(line int32, format string, args ...interface{}) {
} }
} }
var yyerror_lastsyntax int32
func Yyerror(format string, args ...interface{}) { func Yyerror(format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...) yyerrorl(lineno, 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()
}
} }
func Warn(fmt_ string, args ...interface{}) { func Warn(fmt_ string, args ...interface{}) {
@ -1224,7 +1231,7 @@ func ullmancalc(n *Node) {
switch n.Op { switch n.Op {
case OREGISTER, OLITERAL, ONAME: case OREGISTER, OLITERAL, ONAME:
ul = 1 ul = 1
if n.Class == PPARAMREF || (n.Class&PHEAP != 0) { if n.Class == PAUTOHEAP {
ul++ ul++
} }
goto out goto out
@ -2250,6 +2257,7 @@ func isbadimport(path string) bool {
} }
func checknil(x *Node, init *Nodes) { func checknil(x *Node, init *Nodes) {
x = walkexpr(x, nil) // caller has not done this yet
if x.Type.IsInterface() { if x.Type.IsInterface() {
x = Nod(OITAB, x, nil) x = Nod(OITAB, x, nil)
x = typecheck(x, Erv) x = typecheck(x, Erv)

View file

@ -68,11 +68,48 @@ type Node struct {
Used bool Used bool
Isddd bool // is the argument variadic Isddd bool // is the argument variadic
Implicit bool Implicit bool
Addrtaken bool // address taken, even if not moved to heap Addrtaken bool // address taken, even if not moved to heap
Assigned bool // is the variable ever assigned to Assigned bool // is the variable ever assigned to
Likely int8 // likeliness of if statement Likely int8 // likeliness of if statement
Hasbreak bool // has break statement hasVal int8 // +1 for Val, -1 for Opt, 0 for not yet set
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. // Val returns the Val for the node.
@ -117,18 +154,18 @@ func (n *Node) SetOpt(x interface{}) {
n.E = x 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 { type Name struct {
Pack *Node // real package for import . names Pack *Node // real package for import . names
Pkg *Pkg // pkg for OPACK nodes Pkg *Pkg // pkg for OPACK nodes
Heapaddr *Node // temp holding heap address of param Heapaddr *Node // temp holding heap address of param (could move to Param?)
Inlvar *Node // ONAME substitute while inlining Inlvar *Node // ONAME substitute while inlining (could move to Param?)
Defn *Node // initializing assignment Defn *Node // initializing assignment
Curfn *Node // function for local variables 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 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. Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
Iota int32 // value if this name is iota Iota int32 // value if this name is iota
Funcdepth int32 Funcdepth int32
Method bool // OCALLMETH name Method bool // OCALLMETH name
Readonly bool Readonly bool
@ -141,16 +178,83 @@ type Name struct {
type Param struct { type Param struct {
Ntype *Node Ntype *Node
// ONAME func param with PHEAP // ONAME PAUTOHEAP
Outerexpr *Node // expression copied into closure for variable Stackcopy *Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only)
Stackparam *Node // OPARAM node referring to stack copy of param
// ONAME PPARAM // ONAME PPARAM
Field *Field // TFIELD in arg struct Field *Field // TFIELD in arg struct
// ONAME closure param with PPARAMREF // ONAME closure linkage
Outer *Node // outer PPARAMREF in nested closure // Consider:
Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF //
// 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. // Func holds Node fields used only with function-like nodes.
@ -162,9 +266,8 @@ type Func struct {
Dcl []*Node // autodcl for this func/closure Dcl []*Node // autodcl for this func/closure
Inldcl Nodes // copy of dcl for use in inlining Inldcl Nodes // copy of dcl for use in inlining
Closgen int Closgen int
Outerfunc *Node Outerfunc *Node // outer function (for closure)
FieldTrack map[*Sym]struct{} FieldTrack map[*Sym]struct{}
Outer *Node // outer func for closure
Ntype *Node // signature Ntype *Node // signature
Top int // top context (Ecall, Eproc, etc) Top int // top context (Ecall, Eproc, etc)
Closure *Node // OCLOSURE <-> ODCLFUNC Closure *Node // OCLOSURE <-> ODCLFUNC
@ -266,7 +369,7 @@ const (
OINDEX // Left[Right] (index of array or slice) OINDEX // Left[Right] (index of array or slice)
OINDEXMAP // Left[Right] (index of map) OINDEXMAP // Left[Right] (index of map)
OKEY // Left:Right (key:value in struct/array/map literal, or slice index pair) 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) OLEN // len(Left)
OMAKE // make(List) (before type checking converts to one of the following) OMAKE // make(List) (before type checking converts to one of the following)
OMAKECHAN // make(Type, Left) (type is chan) 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 links such structs back to their map type.
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 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. // StructType returns t's extra struct-specific fields.
func (t *Type) StructType() *StructType { func (t *Type) StructType() *StructType {
t.wantEtype(TSTRUCT) t.wantEtype(TSTRUCT)
@ -287,7 +297,7 @@ type SliceType struct {
type Field struct { type Field struct {
Nointerface bool Nointerface bool
Embedded uint8 // embedded field Embedded uint8 // embedded field
Funarg bool Funarg Funarg
Broke bool // broken field definition Broke bool // broken field definition
Isddd bool // field is ... argument 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. // IsFuncArgStruct reports whether t is a struct representing function parameters.
func (t *Type) IsFuncArgStruct() bool { 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 { func (t *Type) Methods() *Fields {

View file

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

View file

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

View file

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

View file

@ -466,7 +466,7 @@ func gmove(f *gc.Node, t *gc.Node) {
//return; //return;
// algorithm is: // algorithm is:
// if small enough, use native int64 -> float64 conversion. // 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 * integer to float
*/ */
@ -496,9 +496,16 @@ func gmove(f *gc.Node, t *gc.Node) {
gmove(&bigi, &rtmp) gmove(&bigi, &rtmp)
gins(mips.AAND, &r1, &rtmp) gins(mips.AAND, &r1, &rtmp)
p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0) 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.Type = obj.TYPE_CONST
p2.From.Offset = 1 p2.From.Offset = 1
p3 := gins(mips.ASRLV, nil, &r1)
p3.From.Type = obj.TYPE_CONST
p3.From.Offset = 1
gins(mips.AOR, &r3, &r1)
gc.Regfree(&r3)
gc.Patch(p1, gc.Pc) gc.Patch(p1, gc.Pc)
} }

View file

@ -316,7 +316,7 @@ func checkFunc(f *Func) {
} }
// domCheck reports whether x dominates y (including x==y). // 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) { if !sdom.isAncestorEq(f.Entry, y) {
// unreachable - ignore // unreachable - ignore
return true return true

View file

@ -86,14 +86,14 @@ func Compile(f *Func) {
// Surround timing information w/ enough context to allow comparisons. // Surround timing information w/ enough context to allow comparisons.
time := tEnd.Sub(tStart).Nanoseconds() time := tEnd.Sub(tStart).Nanoseconds()
if p.time { if p.time {
f.logStat("TIME(ns)", time) f.LogStat("TIME(ns)", time)
} }
if p.mem { if p.mem {
var mEnd runtime.MemStats var mEnd runtime.MemStats
runtime.ReadMemStats(&mEnd) runtime.ReadMemStats(&mEnd)
nBytes := mEnd.TotalAlloc - mStart.TotalAlloc nBytes := mEnd.TotalAlloc - mStart.TotalAlloc
nAllocs := mEnd.Mallocs - mStart.Mallocs 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 { if checkEnabled {
@ -124,6 +124,10 @@ var checkEnabled = false
var IntrinsicsDebug int var IntrinsicsDebug int
var IntrinsicsDisable bool var IntrinsicsDisable bool
var BuildDebug int
var BuildTest int
var BuildStats int
// PhaseOption sets the specified flag in the specified ssa phase, // PhaseOption sets the specified flag in the specified ssa phase,
// returning empty string if this was successful or a string explaining // returning empty string if this was successful or a string explaining
// the error if it was not. // the error if it was not.
@ -174,6 +178,19 @@ func PhaseOption(phase, flag string, val int) string {
} }
return "" 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) underphase := strings.Replace(phase, "_", " ", -1)
var re *regexp.Regexp var re *regexp.Regexp

View file

@ -9,23 +9,25 @@ import (
"crypto/sha1" "crypto/sha1"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
) )
type Config struct { type Config struct {
arch string // "amd64", etc. arch string // "amd64", etc.
IntSize int64 // 4 or 8 IntSize int64 // 4 or 8
PtrSize int64 // 4 or 8 PtrSize int64 // 4 or 8
lowerBlock func(*Block) bool // lowering function lowerBlock func(*Block) bool // lowering function
lowerValue func(*Value, *Config) bool // lowering function lowerValue func(*Value, *Config) bool // lowering function
registers []Register // machine registers registers []Register // machine registers
flagRegMask regMask // flag register mask flagRegMask regMask // flag register mask
fe Frontend // callbacks into compiler frontend fe Frontend // callbacks into compiler frontend
HTML *HTMLWriter // html writer, for debugging HTML *HTMLWriter // html writer, for debugging
ctxt *obj.Link // Generic arch information ctxt *obj.Link // Generic arch information
optimize bool // Do optimization optimize bool // Do optimization
noDuffDevice bool // Don't use Duff's device noDuffDevice bool // Don't use Duff's device
curFunc *Func sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score
curFunc *Func
// TODO: more stuff. Compiler flags of interest, ... // 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) 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 return c
} }
func (c *Config) Frontend() Frontend { return c.fe } func (c *Config) Frontend() Frontend { return c.fe }
func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
// NewFunc returns a new, empty function object. // NewFunc returns a new, empty function object.
// Caller must call f.Free() before calling NewFunc again. // Caller must call f.Free() before calling NewFunc again.
@ -262,3 +281,7 @@ func (c *Config) DebugHashMatch(evname, name string) bool {
} }
return false 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. // if v and w are in the same equivalence class and v dominates w.
rewrite := make([]*Value, f.NumValues()) rewrite := make([]*Value, f.NumValues())
for _, e := range partition { for _, e := range partition {
sort.Sort(sortbyentry{e, f.sdom}) sort.Sort(partitionByDom{e, f.sdom})
for i := 0; i < len(e)-1; i++ { for i := 0; i < len(e)-1; i++ {
// e is sorted by entry value so maximal dominant element should be // e is sorted by domorder, so a maximal dominant element is first in the slice
// found first in the slice
v := e[i] v := e[i]
if v == nil { if v == nil {
continue continue
@ -157,9 +156,7 @@ func cse(f *Func) {
rewrite[w.ID] = v rewrite[w.ID] = v
e[j] = nil e[j] = nil
} else { } else {
// since the blocks are assorted in ascending order by entry number // e is sorted by domorder, so v.Block doesn't dominate any subsequent blocks in e
// once we know that we don't dominate a block we can't dominate any
// 'later' block
break break
} }
} }
@ -190,7 +187,7 @@ func cse(f *Func) {
} }
} }
if f.pass.stats > 0 { 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 return v.ID < w.ID
} }
type sortbyentry struct { type partitionByDom struct {
a []*Value // array of values a []*Value // array of values
sdom sparseTree sdom SparseTree
} }
func (sv sortbyentry) Len() int { return len(sv.a) } func (sv partitionByDom) 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 partitionByDom) 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) Less(i, j int) bool {
v := sv.a[i] v := sv.a[i]
w := sv.a[j] 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 // postorder computes a postorder traversal ordering for the
// basic blocks in f. Unreachable blocks will not appear. // basic blocks in f. Unreachable blocks will not appear.
func postorder(f *Func) []*Block { 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()) mark := make([]markKind, f.NumBlocks())
// result ordering // result ordering
@ -40,7 +40,7 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block {
s = s[:len(s)-1] s = s[:len(s)-1]
mark[b.ID] = done mark[b.ID] = done
if len(ponums) > 0 { if len(ponums) > 0 {
ponums[b.ID] = len(order) ponums[b.ID] = int32(len(order))
} }
order = append(order, b) order = append(order, b)
case notExplored: 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. freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil.
idom []*Block // precomputed immediate dominators 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 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. // context to allow item-by-item comparisons across runs.
// For example: // For example:
// awk 'BEGIN {FS="\t"} $3~/TIME/{sum+=$4} END{print "t(ns)=",sum}' t.log // 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 := "" value := ""
for _, a := range args { for _, a := range args {
value += fmt.Sprintf("\t%v", a) 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. // freeValue frees a value. It must no longer be referenced.

View file

@ -382,9 +382,9 @@ var genericOps = []opData{
{name: "ComplexImag", argLength: 1}, // imag(arg0) {name: "ComplexImag", argLength: 1}, // imag(arg0)
// Strings // Strings
{name: "StringMake", argLength: 2}, // arg0=ptr, arg1=len {name: "StringMake", argLength: 2}, // arg0=ptr, arg1=len
{name: "StringPtr", argLength: 1}, // ptr(arg0) {name: "StringPtr", argLength: 1, typ: "BytePtr"}, // ptr(arg0)
{name: "StringLen", argLength: 1}, // len(arg0) {name: "StringLen", argLength: 1, typ: "Int"}, // len(arg0)
// Interfaces // Interfaces
{name: "IMake", argLength: 2}, // arg0=itab, arg1=data {name: "IMake", argLength: 2}, // arg0=itab, arg1=data
@ -407,7 +407,7 @@ var genericOps = []opData{
{name: "LoadReg", argLength: 1}, {name: "LoadReg", argLength: 1},
// Used during ssa construction. Like Copy, but the arg has not been specified yet. // 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. // Unknown value. Used for Values whose values don't matter because they are dead code.
{name: "Unknown"}, {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: "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: "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: "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 // 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, "// generated with: cd gen; go run *.go")
fmt.Fprintln(w) fmt.Fprintln(w)
fmt.Fprintln(w, "package ssa") fmt.Fprintln(w, "package ssa")
if *genLog {
fmt.Fprintln(w, "import \"fmt\"")
}
fmt.Fprintln(w, "import \"math\"") fmt.Fprintln(w, "import \"math\"")
fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used") 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) genResult(w, arch, result, rule.loc)
if *genLog { 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") fmt.Fprintf(w, "return true\n")
@ -300,7 +297,7 @@ func genRules(arch arch) {
} }
if *genLog { 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") fmt.Fprintf(w, "return true\n")

View file

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

View file

@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered continue // lowered
} }
switch v.Op { 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 continue // ok not to lower
} }
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString() s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()

View file

@ -674,6 +674,7 @@ const (
OpVarDef OpVarDef
OpVarKill OpVarKill
OpVarLive OpVarLive
OpKeepAlive
) )
var opcodeTable = [...]opInfo{ var opcodeTable = [...]opInfo{
@ -6167,6 +6168,7 @@ var opcodeTable = [...]opInfo{
}, },
{ {
name: "FwdRef", name: "FwdRef",
auxType: auxSym,
argLen: 0, argLen: 0,
generic: true, generic: true,
}, },
@ -6193,6 +6195,11 @@ var opcodeTable = [...]opInfo{
argLen: 1, argLen: 1,
generic: true, generic: true,
}, },
{
name: "KeepAlive",
argLen: 2,
generic: true,
},
} }
func (o Op) Asm() obj.As { return opcodeTable[o].asm } 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() b.ReportAllocs()
c := NewConfig("amd64", DummyFrontend{b}, nil, true) c := NewConfig("amd64", DummyFrontend{b}, nil, true)
fun := Fun(c, "entry", bg(size)...) fun := Fun(c, "entry", bg(size)...)
domTree(fun.f)
CheckFunc(fun.f) CheckFunc(fun.f)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
@ -51,7 +51,7 @@ func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) {
b.ReportAllocs() b.ReportAllocs()
c := NewConfig("amd64", DummyFrontend{b}, nil, true) c := NewConfig("amd64", DummyFrontend{b}, nil, true)
fun := Fun(c, "entry", bg(b.N)...) fun := Fun(c, "entry", bg(b.N)...)
domTree(fun.f)
CheckFunc(fun.f) CheckFunc(fun.f)
b.ResetTimer() b.ResetTimer()
for i := 0; i < passCount; i++ { for i := 0; i < passCount; i++ {

View file

@ -515,7 +515,7 @@ func prove(f *Func) {
// getBranch returns the range restrictions added by p // getBranch returns the range restrictions added by p
// when reaching b. p is the immediate dominator of b. // 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 { if p == nil || p.Kind != BlockIf {
return unknown 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 package ssa
import ( import (
"cmd/internal/obj"
"fmt" "fmt"
"unsafe" "unsafe"
) )
@ -456,7 +455,7 @@ func (s *regAllocState) init(f *Func) {
s.allocatable = regMask(1)<<s.numRegs - 1 s.allocatable = regMask(1)<<s.numRegs - 1
s.allocatable &^= 1 << s.SPReg s.allocatable &^= 1 << s.SPReg
s.allocatable &^= 1 << s.SBReg s.allocatable &^= 1 << s.SBReg
if obj.Framepointer_enabled != 0 { if s.f.Config.ctxt.Framepointer_enabled {
s.allocatable &^= 1 << 5 // BP s.allocatable &^= 1 << 5 // BP
} }
if s.f.Config.ctxt.Flag_dynlink { if s.f.Config.ctxt.Flag_dynlink {
@ -941,11 +940,29 @@ func (s *regAllocState) regalloc(f *Func) {
s.advanceUses(v) s.advanceUses(v)
continue 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 regspec := opcodeTable[v.Op].reg
if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 { if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
// No register allocation required (or none specified yet) // No register allocation required (or none specified yet)
s.freeRegs(regspec.clobbers) s.freeRegs(regspec.clobbers)
b.Values = append(b.Values, v) b.Values = append(b.Values, v)
s.advanceUses(v)
continue continue
} }
@ -1311,20 +1328,25 @@ func (s *regAllocState) regalloc(f *Func) {
// Start with live at end. // Start with live at end.
for _, li := range s.live[ss.ID] { for _, li := range s.live[ss.ID] {
if s.isLoopSpillCandidate(loop, s.orig[li.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)) entryCandidates.setBit(li.ID, uint(whichExit))
} }
} }
// Control can also be live. // Control can also be live.
if ss.Control != nil && s.isLoopSpillCandidate(loop, ss.Control) { if ss.Control != nil && s.orig[ss.Control.ID] != nil && s.isLoopSpillCandidate(loop, s.orig[ss.Control.ID]) {
entryCandidates.setBit(ss.Control.ID, uint(whichExit)) entryCandidates.setBit(s.orig[ss.Control.ID].ID, uint(whichExit))
} }
// Walk backwards, filling in locally live values, removing those defined. // Walk backwards, filling in locally live values, removing those defined.
for i := len(ss.Values) - 1; i >= 0; i-- { for i := len(ss.Values) - 1; i >= 0; i-- {
v := ss.Values[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 { for _, a := range v.Args {
if s.isLoopSpillCandidate(loop, a) { aorig := s.orig[a.ID]
entryCandidates.setBit(a.ID, uint(whichExit)) if aorig != nil && s.isLoopSpillCandidate(loop, aorig) {
entryCandidates.setBit(aorig.ID, uint(whichExit))
} }
} }
} }
@ -1524,7 +1546,7 @@ sinking:
} }
if f.pass.stats > 0 { 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") 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 ( import (
"fmt" "fmt"
"math" "math"
"os"
"path/filepath"
) )
func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) { 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. // Note: leave v.Block intact. The Block field is used after clobber.
return true 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 { type sparseMap struct {
dense []sparseEntry dense []sparseEntry
sparse []int sparse []int32
} }
// newSparseMap returns a sparseMap that can map // newSparseMap returns a sparseMap that can map
// integers between 0 and n-1 to int32s. // integers between 0 and n-1 to int32s.
func newSparseMap(n int) *sparseMap { func newSparseMap(n int) *sparseMap {
return &sparseMap{nil, make([]int, n)} return &sparseMap{dense: nil, sparse: make([]int32, n)}
} }
func (s *sparseMap) size() int { func (s *sparseMap) size() int {
@ -29,14 +29,14 @@ func (s *sparseMap) size() int {
func (s *sparseMap) contains(k ID) bool { func (s *sparseMap) contains(k ID) bool {
i := s.sparse[k] 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 // get returns the value for key k, or -1 if k does
// not appear in the map. // not appear in the map.
func (s *sparseMap) get(k ID) int32 { func (s *sparseMap) get(k ID) int32 {
i := s.sparse[k] 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 s.dense[i].val
} }
return -1 return -1
@ -44,12 +44,12 @@ func (s *sparseMap) get(k ID) int32 {
func (s *sparseMap) set(k ID, v int32) { func (s *sparseMap) set(k ID, v int32) {
i := s.sparse[k] 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 s.dense[i].val = v
return return
} }
s.dense = append(s.dense, sparseEntry{k, v}) 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 // 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.") panic("bit index too large.")
} }
i := s.sparse[k] 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 s.dense[i].val |= 1 << v
return return
} }
s.dense = append(s.dense, sparseEntry{k, 1 << v}) 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) { func (s *sparseMap) remove(k ID) {
i := s.sparse[k] 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] y := s.dense[len(s.dense)-1]
s.dense[i] = y s.dense[i] = y
s.sparse[y.key] = i s.sparse[y.key] = i

View file

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

View file

@ -4,7 +4,9 @@
package ssa package ssa
type sparseTreeNode struct { import "fmt"
type SparseTreeNode struct {
child *Block child *Block
sibling *Block sibling *Block
parent *Block parent *Block
@ -20,26 +22,39 @@ type sparseTreeNode struct {
entry, exit int32 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 ( const (
// When used to lookup up definitions in a sparse tree, // When used to lookup up definitions in a sparse tree,
// these adjustments to a block's entry (+adjust) and // these adjustments to a block's entry (+adjust) and
// exit (-adjust) numbers allow a distinction to be made // exit (-adjust) numbers allow a distinction to be made
// between assignments (typically branch-dependent // between assignments (typically branch-dependent
// conditionals) occurring "before" phi functions, the // conditionals) occurring "before" the block (e.g., as inputs
// phi functions, and at the bottom of a block. // to the block and its phi functions), "within" the block,
ADJUST_BEFORE = -1 // defined before phi // and "after" the block.
ADJUST_TOP = 0 // defined by phi AdjustBefore = -1 // defined before phi
ADJUST_BOTTOM = 1 // defined within block 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, // It allows rapid ancestor queries,
// such as whether one block dominates another. // 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) // newSparseTree creates a SparseTree from a block-to-parent map (array indexed by Block.ID)
func newSparseTree(f *Func, parentOf []*Block) sparseTree { func newSparseTree(f *Func, parentOf []*Block) SparseTree {
t := make(sparseTree, f.NumBlocks()) t := make(SparseTree, f.NumBlocks())
for _, b := range f.Blocks { for _, b := range f.Blocks {
n := &t[b.ID] n := &t[b.ID]
if p := parentOf[b.ID]; p != nil { if p := parentOf[b.ID]; p != nil {
@ -80,7 +95,7 @@ func newSparseTree(f *Func, parentOf []*Block) sparseTree {
// root left left right right root // root left left right right root
// 1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18 // 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 // reserve n for entry-1, assign n+1 to entry
n++ n++
t[b.ID].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 // to assign entry and exit numbers in the treewalk, those
// numbers are also consistent with this order (i.e., // numbers are also consistent with this order (i.e.,
// Sibling(x) has entry number larger than x's exit number). // 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 return t[x.ID].sibling
} }
// Child returns a child of x in the dominator tree, or // Child returns a child of x in the dominator tree, or
// nil if there are none. The choice of first child is // nil if there are none. The choice of first child is
// arbitrary but repeatable. // arbitrary but repeatable.
func (t sparseTree) Child(x *Block) *Block { func (t SparseTree) Child(x *Block) *Block {
return t[x.ID].child return t[x.ID].child
} }
// isAncestorEq reports whether x is an ancestor of or equal to y. // 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 { if x == y {
return true return true
} }
@ -125,7 +140,7 @@ func (t sparseTree) isAncestorEq(x, y *Block) bool {
} }
// isAncestor reports whether x is a strict ancestor of y. // 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 { if x == y {
return false return false
} }
@ -134,8 +149,38 @@ func (t sparseTree) isAncestor(x, y *Block) bool {
return xx.entry < yy.entry && yy.exit < xx.exit 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 // domorder returns a value for dominator-oriented sorting.
// if x may dominate y, and false if x cannot dominate y. // Block domination does not provide a total ordering,
func (t sparseTree) maxdomorder(x *Block) int32 { // 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 return t[x.ID].entry
} }

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