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

Change-Id: I0596a40722bf62952bd2eba85ccf3f104de589e4
This commit is contained in:
Dmitri Shuralyov 2020-11-17 18:32:51 -05:00
commit 0985c1bd2d
1335 changed files with 63078 additions and 77546 deletions

View file

@ -31,6 +31,7 @@ Aaron Cannon <cannona@fireantproductions.com>
Aaron France <aaron.l.france@gmail.com> 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 Patterson <tenderlove@ruby-lang.org>
Aaron Stein <aaronstein12@gmail.com> Aaron Stein <aaronstein12@gmail.com>
Aaron Torres <tcboox@gmail.com> Aaron Torres <tcboox@gmail.com>
Aaron Zinman <aaron@azinman.com> Aaron Zinman <aaron@azinman.com>
@ -58,6 +59,7 @@ Adrian Hesketh <adrianhesketh@hushmail.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>
Adrien Delorme <adrien.delorme@icloud.com>
Adrien Petel <peteladrien@gmail.com> Adrien Petel <peteladrien@gmail.com>
Aécio Júnior <aeciodantasjunior@gmail.com> Aécio Júnior <aeciodantasjunior@gmail.com>
Aeneas Rekkas (arekkas) <aeneas@ory.am> Aeneas Rekkas (arekkas) <aeneas@ory.am>
@ -114,6 +116,7 @@ Alex Zhirov <azhirov@google.com>
Alexander Demakin <alexander.demakin@gmail.com> Alexander Demakin <alexander.demakin@gmail.com>
Alexander Döring <email@alexd.ch> Alexander Döring <email@alexd.ch>
Alexander F Rødseth <alexander.rodseth@appeartv.com> Alexander F Rødseth <alexander.rodseth@appeartv.com>
Alexander Greim <alexxx@iltempo.de>
Alexander Guz <kalimatas@gmail.com> Alexander Guz <kalimatas@gmail.com>
Alexander Kauer <alexander@affine.space> Alexander Kauer <alexander@affine.space>
Alexander Kucherenko <alxkchr@gmail.com> Alexander Kucherenko <alxkchr@gmail.com>
@ -122,6 +125,7 @@ Alexander Lourier <aml@rulezz.ru>
Alexander Menzhinsky <amenzhinsky@gmail.com> Alexander Menzhinsky <amenzhinsky@gmail.com>
Alexander Morozov <lk4d4math@gmail.com> Alexander Morozov <lk4d4math@gmail.com>
Alexander Neumann <alexander@bumpern.de> Alexander Neumann <alexander@bumpern.de>
Alexander Nohe <alex.nohe427@gmail.com>
Alexander Orlov <alexander.orlov@loxal.net> Alexander Orlov <alexander.orlov@loxal.net>
Alexander Pantyukhin <apantykhin@gmail.com> Alexander Pantyukhin <apantykhin@gmail.com>
Alexander Polcyn <apolcyn@google.com> Alexander Polcyn <apolcyn@google.com>
@ -149,6 +153,7 @@ Alexey Semenyuk <alexsemenyuk88@gmail.com>
Alexis Hildebrandt <surryhill@gmail.com> Alexis Hildebrandt <surryhill@gmail.com>
Alexis Hunt <lexer@google.com> Alexis Hunt <lexer@google.com>
Alexis Imperial-Legrand <ail@google.com> Alexis Imperial-Legrand <ail@google.com>
Ali Farooq <ali.farooq0@pm.me>
Ali Rizvi-Santiago <arizvisa@gmail.com> Ali Rizvi-Santiago <arizvisa@gmail.com>
Aliaksandr Valialkin <valyala@gmail.com> Aliaksandr Valialkin <valyala@gmail.com>
Alif Rachmawadi <subosito@gmail.com> Alif Rachmawadi <subosito@gmail.com>
@ -156,14 +161,17 @@ Allan Simon <allan.simon@supinfo.com>
Allen Li <ayatane@google.com> Allen Li <ayatane@google.com>
Alok Menghrajani <alok.menghrajani@gmail.com> Alok Menghrajani <alok.menghrajani@gmail.com>
Aman Gupta <aman@tmm1.net> Aman Gupta <aman@tmm1.net>
Amarjeet Anand <amarjeetanandsingh@gmail.com>
Amir Mohammad Saied <amir@gluegadget.com> Amir Mohammad Saied <amir@gluegadget.com>
Amr Mohammed <merodiro@gmail.com> Amr Mohammed <merodiro@gmail.com>
Amrut Joshi <amrut.joshi@gmail.com> Amrut Joshi <amrut.joshi@gmail.com>
An Long <aisk1988@gmail.com>
An Xiao <hac@zju.edu.cn> An Xiao <hac@zju.edu.cn>
Anand K. Mistry <anand@mistry.ninja> Anand K. Mistry <anand@mistry.ninja>
Anders Pearson <anders@columbia.edu> Anders Pearson <anders@columbia.edu>
Anderson Queiroz <contato@andersonq.eti.br> Anderson Queiroz <contato@andersonq.eti.br>
André Carvalho <asantostc@gmail.com> André Carvalho <asantostc@gmail.com>
André Martins <aanm90@gmail.com>
Andre Nathan <andrenth@gmail.com> Andre Nathan <andrenth@gmail.com>
Andrea Nodari <andrea.nodari91@gmail.com> Andrea Nodari <andrea.nodari91@gmail.com>
Andrea Spadaccini <spadaccio@google.com> Andrea Spadaccini <spadaccio@google.com>
@ -187,9 +195,11 @@ Andrew Braunstein <awbraunstein@gmail.com>
Andrew Bursavich <abursavich@gmail.com> Andrew Bursavich <abursavich@gmail.com>
Andrew Ekstedt <andrew.ekstedt@gmail.com> Andrew Ekstedt <andrew.ekstedt@gmail.com>
Andrew Etter <andrew.etter@gmail.com> Andrew Etter <andrew.etter@gmail.com>
Andrew G. Morgan <agm@google.com>
Andrew Gerrand <adg@golang.org> Andrew Gerrand <adg@golang.org>
Andrew Harding <andrew@spacemonkey.com> Andrew Harding <andrew@spacemonkey.com>
Andrew Jackura <ajackura@google.com> Andrew Jackura <ajackura@google.com>
Andrew Louis <alouis@digitalocean.com>
Andrew Lutomirski <andy@luto.us> Andrew Lutomirski <andy@luto.us>
Andrew Medvedev <andrew.y.medvedev@gmail.com> Andrew Medvedev <andrew.y.medvedev@gmail.com>
Andrew Pilloud <andrewpilloud@igneoussystems.com> Andrew Pilloud <andrewpilloud@igneoussystems.com>
@ -219,6 +229,7 @@ Andy Lindeman <andy@lindeman.io>
Andy Maloney <asmaloney@gmail.com> Andy Maloney <asmaloney@gmail.com>
Andy Pan <panjf2000@gmail.com> Andy Pan <panjf2000@gmail.com>
Andy Walker <walkeraj@gmail.com> Andy Walker <walkeraj@gmail.com>
Andy Wang <cbeuw.andy@gmail.com>
Andzej Maciusovic <andzej.maciusovic@gmail.com> Andzej Maciusovic <andzej.maciusovic@gmail.com>
Anfernee Yongkun Gui <anfernee.gui@gmail.com> Anfernee Yongkun Gui <anfernee.gui@gmail.com>
Angelo Bulfone <mbulfone@gmail.com> Angelo Bulfone <mbulfone@gmail.com>
@ -226,6 +237,7 @@ Anh Hai Trinh <anh.hai.trinh@gmail.com>
Anit Gandhi <anitgandhi@gmail.com> Anit Gandhi <anitgandhi@gmail.com>
Ankit Goyal <ankit3goyal@gmail.com> Ankit Goyal <ankit3goyal@gmail.com>
Anmol Sethi <anmol@aubble.com> Anmol Sethi <anmol@aubble.com>
Annirudh Prasad <annirudh@wandb.com>
Anschel Schaffer-Cohen <anschelsc@gmail.com> Anschel Schaffer-Cohen <anschelsc@gmail.com>
Anthony Alves <cvballa3g0@gmail.com> Anthony Alves <cvballa3g0@gmail.com>
Anthony Canino <anthony.canino1@gmail.com> Anthony Canino <anthony.canino1@gmail.com>
@ -239,15 +251,18 @@ Anthony Woods <awoods@raintank.io>
Antoine GIRARD <sapk@sapk.fr> Antoine GIRARD <sapk@sapk.fr>
Antoine Martin <antoine97.martin@gmail.com> Antoine Martin <antoine97.martin@gmail.com>
Anton Gyllenberg <anton@iki.fi> Anton Gyllenberg <anton@iki.fi>
Anton Kuklin <anton.a.kuklin@gmail.com>
Antonin Amand <antonin.amand@gmail.com> Antonin Amand <antonin.amand@gmail.com>
Antonio Antelo <aantelov87@gmail.com> Antonio Antelo <aantelov87@gmail.com>
Antonio Bibiano <antbbn@gmail.com> Antonio Bibiano <antbbn@gmail.com>
Antonio Huete Jimenez <tuxillo@quantumachine.net> Antonio Huete Jimenez <tuxillo@quantumachine.net>
Antonio Murdaca <runcom@redhat.com> Antonio Murdaca <runcom@redhat.com>
Antonio Troina <thoeni@gmail.com> Antonio Troina <thoeni@gmail.com>
Anze Kolar <me@akolar.com>
Aofei Sheng <aofei@aofeisheng.com> Aofei Sheng <aofei@aofeisheng.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>
Araragi Hokuto <kanseihonbucho@protonmail.com>
Arash Bina <arash@arash.io> Arash Bina <arash@arash.io>
Arda Güçlü <ardaguclu@gmail.com> Arda Güçlü <ardaguclu@gmail.com>
Areski Belaid <areski@gmail.com> Areski Belaid <areski@gmail.com>
@ -273,6 +288,7 @@ Audrius Butkevicius <audrius.butkevicius@gmail.com>
Augusto Roman <aroman@gmail.com> Augusto Roman <aroman@gmail.com>
Aulus Egnatius Varialus <varialus@gmail.com> Aulus Egnatius Varialus <varialus@gmail.com>
Aurélien Rainone <aurelien.rainone@gmail.com> Aurélien Rainone <aurelien.rainone@gmail.com>
Aurélio A. Heckert <aurium@gmail.com>
Austin Clements <austin@google.com> <aclements@csail.mit.edu> Austin Clements <austin@google.com> <aclements@csail.mit.edu>
Avi Flax <avi@timehop.com> Avi Flax <avi@timehop.com>
awaw fumin <awawfumin@gmail.com> awaw fumin <awawfumin@gmail.com>
@ -315,6 +331,7 @@ Benoit Sigoure <tsunanet@gmail.com>
Berengar Lehr <Berengar.Lehr@gmx.de> Berengar Lehr <Berengar.Lehr@gmx.de>
Berkant Ipek <41230766+0xbkt@users.noreply.github.com> Berkant Ipek <41230766+0xbkt@users.noreply.github.com>
Bharath Thiruveedula <tbharath91@gmail.com> Bharath Thiruveedula <tbharath91@gmail.com>
Bhavin Gandhi <bhavin7392@gmail.com>
Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@gmail.com> Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@gmail.com>
Bill O'Farrell <billo@ca.ibm.com> Bill O'Farrell <billo@ca.ibm.com>
Bill Prin <waprin@google.com> Bill Prin <waprin@google.com>
@ -322,6 +339,7 @@ Bill Thiede <couchmoney@gmail.com>
Bill Zissimopoulos <billziss@navimatics.com> Bill Zissimopoulos <billziss@navimatics.com>
Billie Harold Cleek <bhcleek@gmail.com> Billie Harold Cleek <bhcleek@gmail.com>
Billy Lynch <wlynch@google.com> Billy Lynch <wlynch@google.com>
Billy Zaelani Malik <m.billyzaelani@gmail.com>
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Bjorn Tillenius <bjorn@tillenius.me> Bjorn Tillenius <bjorn@tillenius.me>
Bjorn Tipling <bjorn.tipling@gmail.com> Bjorn Tipling <bjorn.tipling@gmail.com>
@ -331,12 +349,15 @@ Blake Mesdag <blakemesdag@gmail.com>
Blake Mizerany <blake.mizerany@gmail.com> Blake Mizerany <blake.mizerany@gmail.com>
Blixt <me@blixt.nyc> Blixt <me@blixt.nyc>
Bob Briski <rbriski@gmail.com> Bob Briski <rbriski@gmail.com>
Bob McNaughton <bobmcn@gmail.com>
Bob Potter <bobby.potter@gmail.com> Bob Potter <bobby.potter@gmail.com>
Bobby DeSimone <bobbydesimone@gmail.com> Bobby DeSimone <bobbydesimone@gmail.com>
Bobby Powers <bobbypowers@gmail.com> Bobby Powers <bobbypowers@gmail.com>
Boqin Qin <bobbqqin@gmail.com>
Boris Nagaev <nagaev@google.com> Boris Nagaev <nagaev@google.com>
Borja Clemente <borja.clemente@gmail.com> Borja Clemente <borja.clemente@gmail.com>
Brad Burch <brad.burch@gmail.com> Brad Burch <brad.burch@gmail.com>
Brad Erickson <bderickson@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>
Brad Jones <rbjones@google.com> Brad Jones <rbjones@google.com>
@ -351,6 +372,7 @@ Brandon Bennett <bbennett@fb.com>
Brandon Gilmore <varz@google.com> Brandon Gilmore <varz@google.com>
Brandon Philips <brandon@ifup.org> Brandon Philips <brandon@ifup.org>
Brandon Ryan <bjryan19@gmail.com> Brandon Ryan <bjryan19@gmail.com>
Brayden Cloud <bcloud@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>
Brett Cannon <bcannon@gmail.com> Brett Cannon <bcannon@gmail.com>
@ -390,6 +412,7 @@ Carlos Castillo <cookieo9@gmail.com>
Carlos Cirello <uldericofilho@gmail.com> Carlos Cirello <uldericofilho@gmail.com>
Carlos Eduardo <me@carlosedp.com> Carlos Eduardo <me@carlosedp.com>
Carlos Eduardo Seo <cseo@linux.vnet.ibm.com> Carlos Eduardo Seo <cseo@linux.vnet.ibm.com>
Carlos Iriarte <ciriarte@gmail.com>
Carlos Souza <carloshrsouza@gmail.com> Carlos Souza <carloshrsouza@gmail.com>
Carolyn Van Slyck <me@carolynvanslyck.com> Carolyn Van Slyck <me@carolynvanslyck.com>
Carrie Bynon <cbynon@gmail.com> Carrie Bynon <cbynon@gmail.com>
@ -405,6 +428,7 @@ Chad Rosier <mrosier.qdt@qualcommdatacenter.com>
ChaiShushan <chaishushan@gmail.com> ChaiShushan <chaishushan@gmail.com>
Changkun Ou <hi@changkun.us> Changkun Ou <hi@changkun.us>
Channing Kimble-Brown <channing@golang.org> Channing Kimble-Brown <channing@golang.org>
Chao Xu <xuchao@google.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io> Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charles Kenney <charlesc.kenney@gmail.com> Charles Kenney <charlesc.kenney@gmail.com>
Charles L. Dorian <cldorian@gmail.com> Charles L. Dorian <cldorian@gmail.com>
@ -426,6 +450,7 @@ Chris Howey <howeyc@gmail.com>
Chris Hundt <hundt@google.com> Chris Hundt <hundt@google.com>
Chris Jones <chris@cjones.org> <chris.jones.yar@gmail.com> Chris Jones <chris@cjones.org> <chris.jones.yar@gmail.com>
Chris Kastorff <encryptio@gmail.com> Chris Kastorff <encryptio@gmail.com>
Chris Le Roy <brompwnie@users.noreply.github.com>
Chris Lennert <calennert@gmail.com> Chris Lennert <calennert@gmail.com>
Chris Liles <caveryliles@gmail.com> Chris Liles <caveryliles@gmail.com>
Chris Manghane <cmang@golang.org> Chris Manghane <cmang@golang.org>
@ -475,6 +500,7 @@ Conrad Meyer <cemeyer@cs.washington.edu>
Conrado Gouvea <conradoplg@gmail.com> Conrado Gouvea <conradoplg@gmail.com>
Constantin Konstantinidis <constantinkonstantinidis@gmail.com> Constantin Konstantinidis <constantinkonstantinidis@gmail.com>
Corey Thomasson <cthom.lists@gmail.com> Corey Thomasson <cthom.lists@gmail.com>
Corne van der Plas <vdplas@gmail.com>
Cosmos Nicolaou <cnicolaou@google.com> Cosmos Nicolaou <cnicolaou@google.com>
Costin Chirvasuta <ctin@google.com> Costin Chirvasuta <ctin@google.com>
Craig Citro <craigcitro@google.com> Craig Citro <craigcitro@google.com>
@ -506,9 +532,11 @@ Daniel Ingram <ingramds@appstate.edu>
Daniel Johansson <dajo2002@gmail.com> Daniel Johansson <dajo2002@gmail.com>
Daniel Kerwin <d.kerwin@gini.net> Daniel Kerwin <d.kerwin@gini.net>
Daniel Krech <eikeon@eikeon.com> Daniel Krech <eikeon@eikeon.com>
Daniel Kumor <rdkumor@gmail.com>
Daniel Langner <s8572327@gmail.com> Daniel Langner <s8572327@gmail.com>
Daniel Lidén <daniel.liden.87@gmail.com> Daniel Lidén <daniel.liden.87@gmail.com>
Daniel Lublin <daniel@lublin.se> Daniel Lublin <daniel@lublin.se>
Daniel Mangum <georgedanielmangum@gmail.com>
Daniel Martí <mvdan@mvdan.cc> Daniel Martí <mvdan@mvdan.cc>
Daniel Morsing <daniel.morsing@gmail.com> Daniel Morsing <daniel.morsing@gmail.com>
Daniel Nadasi <dnadasi@google.com> Daniel Nadasi <dnadasi@google.com>
@ -519,6 +547,8 @@ Daniel Speichert <daniel@speichert.pl>
Daniel Theophanes <kardianos@gmail.com> Daniel Theophanes <kardianos@gmail.com>
Daniel Upton <daniel@floppy.co> Daniel Upton <daniel@floppy.co>
Daniela Petruzalek <daniela.petruzalek@gmail.com> Daniela Petruzalek <daniela.petruzalek@gmail.com>
Danish Dua <danishdua@google.com>
Danish Prakash <grafitykoncept@gmail.com>
Danny Rosseau <daniel.rosseau@gmail.com> Danny Rosseau <daniel.rosseau@gmail.com>
Daria Kolistratova <daria.kolistratova@intel.com> Daria Kolistratova <daria.kolistratova@intel.com>
Darien Raymond <admin@v2ray.com> Darien Raymond <admin@v2ray.com>
@ -542,6 +572,7 @@ 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 Carlier <devnexen@gmail.com> David Carlier <devnexen@gmail.com>
David Carter <fresco.raja@gmail.com>
David Chase <drchase@google.com> David Chase <drchase@google.com>
David Covert <davidhcovert@gmail.com> David Covert <davidhcovert@gmail.com>
David Crawshaw <david.crawshaw@zentus.com> <crawshaw@google.com> <crawshaw@golang.org> David Crawshaw <david.crawshaw@zentus.com> <crawshaw@google.com> <crawshaw@golang.org>
@ -550,6 +581,7 @@ David Finkel <david.finkel@gmail.com>
David Forsythe <dforsythe@gmail.com> David Forsythe <dforsythe@gmail.com>
David G. Andersen <dave.andersen@gmail.com> David G. Andersen <dave.andersen@gmail.com>
David Glasser <glasser@meteor.com> David Glasser <glasser@meteor.com>
David Golden <david@autopragmatic.com>
David Heuschmann <heuschmann.d@gmail.com> David Heuschmann <heuschmann.d@gmail.com>
David Howden <dhowden@gmail.com> David Howden <dhowden@gmail.com>
David Hubbard <dsp@google.com> David Hubbard <dsp@google.com>
@ -574,6 +606,7 @@ David Volquartz Lebech <david@lebech.info>
David Wimmer <davidlwimmer@gmail.com> David Wimmer <davidlwimmer@gmail.com>
Davies Liu <davies.liu@gmail.com> Davies Liu <davies.liu@gmail.com>
Davor Kapsa <davor.kapsa@gmail.com> Davor Kapsa <davor.kapsa@gmail.com>
Dean Eigenmann <7621705+decanus@users.noreply.github.com>
Dean Prichard <dean.prichard@gmail.com> Dean Prichard <dean.prichard@gmail.com>
Deepak Jois <deepak.jois@gmail.com> Deepak Jois <deepak.jois@gmail.com>
Denis Bernard <db047h@gmail.com> Denis Bernard <db047h@gmail.com>
@ -619,6 +652,7 @@ Dmitry Mottl <dmitry.mottl@gmail.com>
Dmitry Neverov <dmitry.neverov@gmail.com> Dmitry Neverov <dmitry.neverov@gmail.com>
Dmitry Savintsev <dsavints@gmail.com> Dmitry Savintsev <dsavints@gmail.com>
Dmitry Yakunin <nonamezeil@gmail.com> Dmitry Yakunin <nonamezeil@gmail.com>
Doga Fincan <doga@icloud.com>
Domas Tamašauskas <puerdomus@gmail.com> Domas Tamašauskas <puerdomus@gmail.com>
Domen Ipavec <domen@ipavec.net> Domen Ipavec <domen@ipavec.net>
Dominic Green <dominicgreen1@gmail.com> Dominic Green <dominicgreen1@gmail.com>
@ -642,6 +676,7 @@ 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>
Dylan Waits <dylan@waits.io> Dylan Waits <dylan@waits.io>
Ed Schouten <ed@nuxi.nl>
Edan Bedrik <3d4nb3@gmail.com> Edan Bedrik <3d4nb3@gmail.com>
Eddie Scholtz <escholtz@google.com> Eddie Scholtz <escholtz@google.com>
Eden Li <eden.li@gmail.com> Eden Li <eden.li@gmail.com>
@ -659,11 +694,13 @@ Elena Grahovac <elena@grahovac.me>
Eli Bendersky <eliben@google.com> Eli Bendersky <eliben@google.com>
Elias Naur <mail@eliasnaur.com> <elias.naur@gmail.com> Elias Naur <mail@eliasnaur.com> <elias.naur@gmail.com>
Elliot Morrison-Reed <elliotmr@gmail.com> Elliot Morrison-Reed <elliotmr@gmail.com>
Ellison Leão <ellisonleao@gmail.com>
Emerson Lin <linyintor@gmail.com> Emerson Lin <linyintor@gmail.com>
Emil Hessman <emil@hessman.se> Emil Hessman <emil@hessman.se>
Emil Mursalimov <mursalimovemeel@gmail.com> Emil Mursalimov <mursalimovemeel@gmail.com>
Emilien Kenler <hello@emilienkenler.com> Emilien Kenler <hello@emilienkenler.com>
Emmanuel Odeke <emm.odeke@gmail.com> <odeke@ualberta.ca> Emmanuel Odeke <emm.odeke@gmail.com> <odeke@ualberta.ca>
Emrecan Bati <emrecanbati@gmail.com>
Eno Compton <enocom@google.com> Eno Compton <enocom@google.com>
Eoghan Sherry <ejsherry@gmail.com> Eoghan Sherry <ejsherry@gmail.com>
Eric Biggers <ebiggers@google.com> Eric Biggers <ebiggers@google.com>
@ -682,6 +719,7 @@ Eric Rescorla <ekr@rtfm.com>
Eric Roshan-Eisner <eric.d.eisner@gmail.com> Eric Roshan-Eisner <eric.d.eisner@gmail.com>
Eric Rutherford <erutherford@gmail.com> Eric Rutherford <erutherford@gmail.com>
Eric Rykwalder <e.rykwalder@gmail.com> Eric Rykwalder <e.rykwalder@gmail.com>
Erick Tryzelaar <etryzelaar@google.com>
Erik Aigner <aigner.erik@gmail.com> 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>
@ -694,6 +732,7 @@ Esko Luontola <esko.luontola@gmail.com>
Ethan Burns <eaburns@google.com> Ethan Burns <eaburns@google.com>
Ethan Miller <eamiller@us.ibm.com> Ethan Miller <eamiller@us.ibm.com>
Euan Kemp <euank@euank.com> Euan Kemp <euank@euank.com>
Eugene Formanenko <mo4islona@gmail.com>
Eugene Kalinin <e.v.kalinin@gmail.com> Eugene Kalinin <e.v.kalinin@gmail.com>
Evan Broder <evan@stripe.com> Evan Broder <evan@stripe.com>
Evan Brown <evanbrown@google.com> Evan Brown <evanbrown@google.com>
@ -705,6 +744,7 @@ Evan Kroske <evankroske@google.com>
Evan Martin <evan.martin@gmail.com> Evan Martin <evan.martin@gmail.com>
Evan Phoenix <evan@phx.io> Evan Phoenix <evan@phx.io>
Evan Shaw <chickencha@gmail.com> Evan Shaw <chickencha@gmail.com>
Evgeniy Kulikov <tuxuls@gmail.com>
Evgeniy Polyakov <zbr@ioremap.net> Evgeniy Polyakov <zbr@ioremap.net>
Ewan Chou <coocood@gmail.com> Ewan Chou <coocood@gmail.com>
Ewan Valentine <ewan.valentine89@gmail.com> Ewan Valentine <ewan.valentine89@gmail.com>
@ -725,8 +765,10 @@ Fedor Indutny <fedor@indutny.com>
Fedor Korotkiy <dartslon@gmail.com> Fedor Korotkiy <dartslon@gmail.com>
Felipe Oliveira <felipeweb.programador@gmail.com> Felipe Oliveira <felipeweb.programador@gmail.com>
Felix Bünemann <Felix.Buenemann@gmail.com> Felix Bünemann <Felix.Buenemann@gmail.com>
Felix Cornelius <9767036+fcornelius@users.noreply.github.com>
Felix Geisendörfer <haimuiba@gmail.com> Felix Geisendörfer <haimuiba@gmail.com>
Felix Kollmann <fk@konsorten.de> Felix Kollmann <fk@konsorten.de>
Ferenc Szabo <frncmx@gmail.com>
Filip Gruszczyński <gruszczy@gmail.com> Filip Gruszczyński <gruszczy@gmail.com>
Filip Haglund <drathier@users.noreply.github.com> Filip Haglund <drathier@users.noreply.github.com>
Filip Stanis <fstanis@google.com> Filip Stanis <fstanis@google.com>
@ -774,6 +816,7 @@ 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>
Gauthier Jolly <gauthier.jolly@gmail.com> Gauthier Jolly <gauthier.jolly@gmail.com>
Gawen Arab <gawen.arab@c.zen.ly>
Geert-Johan Riemer <gjr19912@gmail.com> Geert-Johan Riemer <gjr19912@gmail.com>
Genevieve Luyt <genevieve.luyt@gmail.com> Genevieve Luyt <genevieve.luyt@gmail.com>
Gengliang Wang <ltnwgl@gmail.com> Gengliang Wang <ltnwgl@gmail.com>
@ -795,6 +838,7 @@ Gianguido Sora` <g.sora4@gmail.com>
Gideon Jan-Wessel Redelinghuys <gjredelinghuys@gmail.com> Gideon Jan-Wessel Redelinghuys <gjredelinghuys@gmail.com>
Giles Lean <giles.lean@pobox.com> Giles Lean <giles.lean@pobox.com>
Giovanni Bajo <rasky@develer.com> Giovanni Bajo <rasky@develer.com>
GitHub User @aca (50316549) <acadx0@gmail.com>
GitHub User @ajnirp (1688456) <ajnirp@users.noreply.github.com> GitHub User @ajnirp (1688456) <ajnirp@users.noreply.github.com>
GitHub User @ajz01 (4744634) <ajzdenek@gmail.com> GitHub User @ajz01 (4744634) <ajzdenek@gmail.com>
GitHub User @alkesh26 (1019076) <alkesh26@gmail.com> GitHub User @alkesh26 (1019076) <alkesh26@gmail.com>
@ -805,12 +849,18 @@ GitHub User @bakape (7851952) <bakape@gmail.com>
GitHub User @bgadrian (830001) <aditza8@gmail.com> GitHub User @bgadrian (830001) <aditza8@gmail.com>
GitHub User @bontequero (2674999) <bontequero@gmail.com> GitHub User @bontequero (2674999) <bontequero@gmail.com>
GitHub User @cch123 (384546) <buaa.cch@gmail.com> GitHub User @cch123 (384546) <buaa.cch@gmail.com>
GitHub User @chainhelen (7046329) <chainhelen@gmail.com>
GitHub User @chanxuehong (3416908) <chanxuehong@gmail.com> GitHub User @chanxuehong (3416908) <chanxuehong@gmail.com>
GitHub User @cncal (23520240) <flycalvin@qq.com>
GitHub User @DQNEO (188741) <dqneoo@gmail.com> GitHub User @DQNEO (188741) <dqneoo@gmail.com>
GitHub User @Dreamacro (8615343) <chuainian@gmail.com>
GitHub User @dupoxy (1143957) <dupoxy@users.noreply.github.com> GitHub User @dupoxy (1143957) <dupoxy@users.noreply.github.com>
GitHub User @erifan (31343225) <eric.fang@arm.com> GitHub User @erifan (31343225) <eric.fang@arm.com>
GitHub User @esell (9735165) <eujon.sellers@gmail.com> GitHub User @esell (9735165) <eujon.sellers@gmail.com>
GitHub User @fatedier (7346661) <fatedier@gmail.com>
GitHub User @frennkie (6499251) <mail@rhab.de> GitHub User @frennkie (6499251) <mail@rhab.de>
GitHub User @geedchin (11672310) <geedchin@gmail.com>
GitHub User @GrigoriyMikhalkin (3637857) <grigoriymikhalkin@gmail.com>
GitHub User @hengwu0 (41297446) <41297446+hengwu0@users.noreply.github.com> GitHub User @hengwu0 (41297446) <41297446+hengwu0@users.noreply.github.com>
GitHub User @itchyny (375258) <itchyny@hatena.ne.jp> GitHub User @itchyny (375258) <itchyny@hatena.ne.jp>
GitHub User @jinmiaoluo (39730824) <jinmiaoluo@icloud.com> GitHub User @jinmiaoluo (39730824) <jinmiaoluo@icloud.com>
@ -820,11 +870,13 @@ GitHub User @kc1212 (1093806) <kc1212@users.noreply.github.com>
GitHub User @Kropekk (13366453) <kamilkropiewnicki@gmail.com> GitHub User @Kropekk (13366453) <kamilkropiewnicki@gmail.com>
GitHub User @linguohua (3434367) <lghchinaidea@gmail.com> GitHub User @linguohua (3434367) <lghchinaidea@gmail.com>
GitHub User @LotusFenn (13775899) <fenn.lotus@gmail.com> GitHub User @LotusFenn (13775899) <fenn.lotus@gmail.com>
GitHub User @ly303550688 (11519839) <yang.liu636@gmail.com>
GitHub User @madiganz (18340029) <zacharywmadigan@gmail.com> GitHub User @madiganz (18340029) <zacharywmadigan@gmail.com>
GitHub User @maltalex (10195391) <code@bit48.net> GitHub User @maltalex (10195391) <code@bit48.net>
GitHub User @Matts966 (28551465) <Matts966@users.noreply.github.com> GitHub User @Matts966 (28551465) <Matts966@users.noreply.github.com>
GitHub User @micnncim (21333876) <micnncim@gmail.com> GitHub User @micnncim (21333876) <micnncim@gmail.com>
GitHub User @mkishere (224617) <224617+mkishere@users.noreply.github.com> GitHub User @mkishere (224617) <224617+mkishere@users.noreply.github.com>
GitHub User @nu50218 (40682920) <nu_ll@icloud.com>
GitHub User @OlgaVlPetrova (44112727) <OVPpetrova@gmail.com> GitHub User @OlgaVlPetrova (44112727) <OVPpetrova@gmail.com>
GitHub User @pityonline (438222) <pityonline@gmail.com> GitHub User @pityonline (438222) <pityonline@gmail.com>
GitHub User @po3rin (29445112) <abctail30@gmail.com> GitHub User @po3rin (29445112) <abctail30@gmail.com>
@ -836,6 +888,7 @@ GitHub User @shogo-ma (9860598) <Choroma194@gmail.com>
GitHub User @skanehira (7888591) <sho19921005@gmail.com> GitHub User @skanehira (7888591) <sho19921005@gmail.com>
GitHub User @tatsumack (4510569) <tatsu.mack@gmail.com> GitHub User @tatsumack (4510569) <tatsu.mack@gmail.com>
GitHub User @tell-k (26263) <ffk2005@gmail.com> GitHub User @tell-k (26263) <ffk2005@gmail.com>
GitHub User @tennashi (10219626) <tennashio@gmail.com>
GitHub User @uhei (2116845) <uhei@users.noreply.github.com> GitHub User @uhei (2116845) <uhei@users.noreply.github.com>
GitHub User @uropek (39370426) <uropek@gmail.com> GitHub User @uropek (39370426) <uropek@gmail.com>
GitHub User @utkarsh-extc (53217283) <utkarsh.extc@gmail.com> GitHub User @utkarsh-extc (53217283) <utkarsh.extc@gmail.com>
@ -861,6 +914,7 @@ Greg Thelen <gthelen@google.com>
Greg Ward <greg@gerg.ca> Greg Ward <greg@gerg.ca>
Grégoire Delattre <gregoire.delattre@gmail.com> Grégoire Delattre <gregoire.delattre@gmail.com>
Gregory Man <man.gregory@gmail.com> Gregory Man <man.gregory@gmail.com>
Gregory Petrosyan <gregory.petrosyan@gmail.com>
Guilherme Caruso <gui.martinscaruso@gmail.com> Guilherme Caruso <gui.martinscaruso@gmail.com>
Guilherme Garnier <guilherme.garnier@gmail.com> Guilherme Garnier <guilherme.garnier@gmail.com>
Guilherme Goncalves <guilhermeaugustosg@gmail.com> Guilherme Goncalves <guilhermeaugustosg@gmail.com>
@ -917,6 +971,7 @@ Hitoshi Mitake <mitake.hitoshi@gmail.com>
Holden Huang <ttyh061@gmail.com> Holden Huang <ttyh061@gmail.com>
Hong Ruiqi <hongruiqi@gmail.com> Hong Ruiqi <hongruiqi@gmail.com>
Hongfei Tan <feilengcui008@gmail.com> Hongfei Tan <feilengcui008@gmail.com>
Horacio Duran <horacio.duran@gmail.com>
Horst Rutter <hhrutter@gmail.com> Horst Rutter <hhrutter@gmail.com>
Hossein Sheikh Attar <hattar@google.com> Hossein Sheikh Attar <hattar@google.com>
Howard Zhang <howard.zhang@arm.com> Howard Zhang <howard.zhang@arm.com>
@ -927,6 +982,7 @@ Huan Du <i@huandu.me>
Hugues Bruant <hugues.bruant@gmail.com> Hugues Bruant <hugues.bruant@gmail.com>
Huy Le <huy.dinh.le.89@gmail.com> Huy Le <huy.dinh.le.89@gmail.com>
Hyang-Ah Hana Kim <hakim@google.com> <hyangah@gmail.com> Hyang-Ah Hana Kim <hakim@google.com> <hyangah@gmail.com>
Hyoyoung Chang <hyoyoung@gmail.com>
Ian Cottrell <iancottrell@google.com> Ian Cottrell <iancottrell@google.com>
Ian Davis <nospam@iandavis.com> Ian Davis <nospam@iandavis.com>
Ian Gudger <ian@loosescre.ws> Ian Gudger <ian@loosescre.ws>
@ -986,6 +1042,7 @@ Jake B <doogie1012@gmail.com>
Jakob Borg <jakob@nym.se> Jakob Borg <jakob@nym.se>
Jakob Weisblat <jakobw@mit.edu> Jakob Weisblat <jakobw@mit.edu>
Jakub Čajka <jcajka@redhat.com> Jakub Čajka <jcajka@redhat.com>
Jakub Kaczmarzyk <jakubk@mit.edu>
Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com> Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com>
Jamal Carvalho <jamal.a.carvalho@gmail.com> Jamal Carvalho <jamal.a.carvalho@gmail.com>
James Aguilar <jaguilar@google.com> James Aguilar <jaguilar@google.com>
@ -1032,6 +1089,7 @@ Jan Steinke <jan.steinke@gmail.com>
Jan Ziak <0xe2.0x9a.0x9b@gmail.com> Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
Jani Monoses <jani.monoses@ubuntu.com> <jani.monoses@gmail.com> Jani Monoses <jani.monoses@ubuntu.com> <jani.monoses@gmail.com>
Jannis Andrija Schnitzer <jannis@schnitzer.im> Jannis Andrija Schnitzer <jannis@schnitzer.im>
Jared Allard <jaredallard@users.noreply.github.com>
Jared Culp <jculp14@gmail.com> Jared Culp <jculp14@gmail.com>
Jaroslavas Počepko <jp@webmaster.ms> Jaroslavas Počepko <jp@webmaster.ms>
Jason A. Donenfeld <Jason@zx2c4.com> Jason A. Donenfeld <Jason@zx2c4.com>
@ -1086,8 +1144,11 @@ Jerrin Shaji George <jerrinsg@gmail.com>
Jess Frazelle <me@jessfraz.com> Jess Frazelle <me@jessfraz.com>
Jesse Szwedko <jesse.szwedko@gmail.com> Jesse Szwedko <jesse.szwedko@gmail.com>
Jesús Espino <jespinog@gmail.com> Jesús Espino <jespinog@gmail.com>
Jia Zhan <jzhan@uber.com>
Jiacai Liu <jiacai2050@gmail.com>
Jianing Yu <jnyu@google.com> Jianing Yu <jnyu@google.com>
Jianqiao Li <jianqiaoli@google.com> Jianqiao Li <jianqiaoli@google.com>
Jie Ma <jienius@outlook.com>
Jihyun Yu <yjh0502@gmail.com> Jihyun Yu <yjh0502@gmail.com>
Jim Cote <jfcote87@gmail.com> Jim Cote <jfcote87@gmail.com>
Jim Kingdon <jim@bolt.me> Jim Kingdon <jim@bolt.me>
@ -1135,6 +1196,7 @@ John Howard Palevich <jack.palevich@gmail.com>
John Jeffery <jjeffery@sp.com.au> John Jeffery <jjeffery@sp.com.au>
John Jenkins <twodopeshaggy@gmail.com> John Jenkins <twodopeshaggy@gmail.com>
John Leidegren <john.leidegren@gmail.com> John Leidegren <john.leidegren@gmail.com>
John McCabe <john@johnmccabe.net>
John Moore <johnkenneth.moore@gmail.com> John Moore <johnkenneth.moore@gmail.com>
John Newlin <jnewlin@google.com> John Newlin <jnewlin@google.com>
John Papandriopoulos <jpap.code@gmail.com> John Papandriopoulos <jpap.code@gmail.com>
@ -1146,6 +1208,7 @@ John Tuley <john@tuley.org>
John Weldon <johnweldon4@gmail.com> John Weldon <johnweldon4@gmail.com>
Johnny Luo <johnnyluo1980@gmail.com> Johnny Luo <johnnyluo1980@gmail.com>
Jon Chen <jchen@justin.tv> Jon Chen <jchen@justin.tv>
Jon Johnson <jonjohnson@google.com>
Jonas Bernoulli <jonas@bernoul.li> Jonas Bernoulli <jonas@bernoul.li>
Jonathan Allie <jonallie@google.com> Jonathan Allie <jonallie@google.com>
Jonathan Amsterdam <jba@google.com> Jonathan Amsterdam <jba@google.com>
@ -1165,6 +1228,7 @@ Jonathon Lacher <jonathon.lacher@gmail.com>
Jongmin Kim <atomaths@gmail.com> Jongmin Kim <atomaths@gmail.com>
Joonas Kuorilehto <joneskoo@derbian.fi> Joonas Kuorilehto <joneskoo@derbian.fi>
Joop Kiefte <ikojba@gmail.com> <joop@kiefte.net> Joop Kiefte <ikojba@gmail.com> <joop@kiefte.net>
Jordan Christiansen <xordspar0@gmail.com>
Jordan Krage <jmank88@gmail.com> Jordan Krage <jmank88@gmail.com>
Jordan Lewis <jordanthelewis@gmail.com> Jordan Lewis <jordanthelewis@gmail.com>
Jordan Liggitt <liggitt@google.com> Jordan Liggitt <liggitt@google.com>
@ -1177,6 +1241,7 @@ Josa Gesell <josa@gesell.me>
Jose Luis Vázquez González <josvazg@gmail.com> Jose Luis Vázquez González <josvazg@gmail.com>
Joseph Bonneau <jcb@google.com> Joseph Bonneau <jcb@google.com>
Joseph Holsten <joseph@josephholsten.com> Joseph Holsten <joseph@josephholsten.com>
Josh Baum <joshbaum@google.com>
Josh Bleecher Snyder <josharian@gmail.com> Josh Bleecher Snyder <josharian@gmail.com>
Josh Chorlton <jchorlton@gmail.com> Josh Chorlton <jchorlton@gmail.com>
Josh Deprez <josh.deprez@gmail.com> Josh Deprez <josh.deprez@gmail.com>
@ -1185,8 +1250,10 @@ Josh Hoak <jhoak@google.com>
Josh Holland <jrh@joshh.co.uk> Josh Holland <jrh@joshh.co.uk>
Josh Roppo <joshroppo@gmail.com> Josh Roppo <joshroppo@gmail.com>
Josh Varga <josh.varga@gmail.com> Josh Varga <josh.varga@gmail.com>
Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com>
Joshua Boelter <joshua.boelter@intel.com> Joshua Boelter <joshua.boelter@intel.com>
Joshua Chase <jcjoshuachase@gmail.com> Joshua Chase <jcjoshuachase@gmail.com>
Joshua Crowgey <jcrowgey@uw.edu>
Joshua M. Clulow <josh.clulow@joyent.com> Joshua M. Clulow <josh.clulow@joyent.com>
Joshua Rubin <joshua@rubixconsulting.com> Joshua Rubin <joshua@rubixconsulting.com>
Josselin Costanzi <josselin@costanzi.fr> Josselin Costanzi <josselin@costanzi.fr>
@ -1265,6 +1332,7 @@ Kenji Yano <kenji.yano@gmail.com>
Kenneth Shaw <kenshaw@gmail.com> Kenneth Shaw <kenshaw@gmail.com>
Kenny Grant <kennygrant@gmail.com> Kenny Grant <kennygrant@gmail.com>
Kenta Mori <zoncoen@gmail.com> Kenta Mori <zoncoen@gmail.com>
Kerollos Magdy <kerolloz@yahoo.com>
Ketan Parmar <ketanbparmar@gmail.com> Ketan Parmar <ketanbparmar@gmail.com>
Kevan Swanberg <kevswanberg@gmail.com> Kevan Swanberg <kevswanberg@gmail.com>
Kevin Ballard <kevin@sb.org> Kevin Ballard <kevin@sb.org>
@ -1277,10 +1345,14 @@ Kevin Malachowski <chowski@google.com>
Kevin Ruffin <kruffin@gmail.com> Kevin Ruffin <kruffin@gmail.com>
Kevin Vu <kevin.m.vu@gmail.com> Kevin Vu <kevin.m.vu@gmail.com>
Kevin Zita <bleedgreenandgold@gmail.com> Kevin Zita <bleedgreenandgold@gmail.com>
Keyan Pishdadian <kpishdadian@gmail.com>
Kezhu Wang <kezhuw@gmail.com>
Khosrow Moossavi <khos2ow@gmail.com>
Kieran Colford <kieran@kcolford.com> Kieran Colford <kieran@kcolford.com>
Kim Shrier <kshrier@racktopsystems.com> Kim Shrier <kshrier@racktopsystems.com>
Kim Yongbin <kybinz@gmail.com> Kim Yongbin <kybinz@gmail.com>
Kir Kolyshkin <kolyshkin@gmail.com> Kir Kolyshkin <kolyshkin@gmail.com>
Kirill Korotaev <kirillx@gmail.com>
Kirill Motkov <Motkov.Kirill@gmail.com> Kirill Motkov <Motkov.Kirill@gmail.com>
Kirill Smelkov <kirr@nexedi.com> Kirill Smelkov <kirr@nexedi.com>
Kirill Tatchihin <kirabsuir@gmail.com> Kirill Tatchihin <kirabsuir@gmail.com>
@ -1308,6 +1380,7 @@ Kyle Consalus <consalus@gmail.com>
Kyle Isom <kyle@gokyle.net> Kyle Isom <kyle@gokyle.net>
Kyle Jones <kyle@kyledj.com> Kyle Jones <kyle@kyledj.com>
Kyle Lemons <kyle@kylelemons.net> <kevlar@google.com> Kyle Lemons <kyle@kylelemons.net> <kevlar@google.com>
Kyle Nusbaum <kyle@datadog.com>
Kyle Shannon <kyle@pobox.com> Kyle Shannon <kyle@pobox.com>
Kyle Spiers <eiais@google.com> Kyle Spiers <eiais@google.com>
Kyle Wood <kyle@kylewood.cc> Kyle Wood <kyle@kylewood.cc>
@ -1339,6 +1412,8 @@ Leonardo Comelli <leonardo.comelli@gmail.com>
Leonel Quinteros <leonel.quinteros@gmail.com> Leonel Quinteros <leonel.quinteros@gmail.com>
Lev Shamardin <shamardin@gmail.com> Lev Shamardin <shamardin@gmail.com>
Lewin Bormann <lewin.bormann@gmail.com> Lewin Bormann <lewin.bormann@gmail.com>
Liam Haworth <liam@haworth.id.au>
Lily Chung <lilithkchung@gmail.com>
Lion Yang <lion@aosc.xyz> Lion Yang <lion@aosc.xyz>
Liz Rice <liz@lizrice.com> Liz Rice <liz@lizrice.com>
Lloyd Dewolf <foolswisdom@gmail.com> Lloyd Dewolf <foolswisdom@gmail.com>
@ -1396,6 +1471,7 @@ Marcel van Lohuizen <mpvl@golang.org>
Marcelo Cantos <marcelo.cantos@gmail.com> Marcelo Cantos <marcelo.cantos@gmail.com>
Marcelo E. Magallon <marcelo.magallon@gmail.com> Marcelo E. Magallon <marcelo.magallon@gmail.com>
Marco Hennings <marco.hennings@freiheit.com> Marco Hennings <marco.hennings@freiheit.com>
Marcus Weiner <marcus.weiner@gmail.com>
Marcus Willock <crazcalm@gmail.com> Marcus Willock <crazcalm@gmail.com>
Marga Manterola <marga@google.com> Marga Manterola <marga@google.com>
Mariano Cano <mariano@smallstep.com> Mariano Cano <mariano@smallstep.com>
@ -1426,6 +1502,7 @@ 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>
Marten Seemann <martenseemann@gmail.com> Marten Seemann <martenseemann@gmail.com>
Martin Asquino <martin.asquino@gmail.com>
Martin Bertschler <mbertschler@gmail.com> Martin Bertschler <mbertschler@gmail.com>
Martin Garton <garton@gmail.com> Martin Garton <garton@gmail.com>
Martin Habbecke <marhab@google.com> Martin Habbecke <marhab@google.com>
@ -1449,6 +1526,7 @@ Maryan Hratson <gmarik@gmail.com>
Masahiro Furudate <masahiro.furudate@gmail.com> Masahiro Furudate <masahiro.furudate@gmail.com>
Masahiro Wakame <vvakame@gmail.com> Masahiro Wakame <vvakame@gmail.com>
Masaki Yoshida <yoshida.masaki@gmail.com> Masaki Yoshida <yoshida.masaki@gmail.com>
Masaya Watanabe <sfbgwm30@gmail.com>
Mat Byczkowski <mbyczkowski@gmail.com> Mat Byczkowski <mbyczkowski@gmail.com>
Mat Ryer <thatmatryer@gmail.com> Mat Ryer <thatmatryer@gmail.com>
Máté Gulyás <mgulyas86@gmail.com> Máté Gulyás <mgulyas86@gmail.com>
@ -1495,6 +1573,7 @@ Max Ushakov <ushmax@gmail.com>
Maxim Eryomenko <moeryomenko@gmail.com> Maxim Eryomenko <moeryomenko@gmail.com>
Maxim Khitrov <max@mxcrypt.com> Maxim Khitrov <max@mxcrypt.com>
Maxim Pimenov <mpimenov@google.com> Maxim Pimenov <mpimenov@google.com>
Maxim Pugachev <pugachev.mm@gmail.com>
Maxim Ushakov <ushakov@google.com> Maxim Ushakov <ushakov@google.com>
Maxime de Roucy <maxime.deroucy@gmail.com> Maxime de Roucy <maxime.deroucy@gmail.com>
Máximo Cuadros Ortiz <mcuadros@gmail.com> Máximo Cuadros Ortiz <mcuadros@gmail.com>
@ -1549,6 +1628,7 @@ 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>
Michal Franc <lam.michal.franc@gmail.com> Michal Franc <lam.michal.franc@gmail.com>
Michał Łowicki <mlowicki@gmail.com>
Michal Pristas <michal.pristas@gmail.com> Michal Pristas <michal.pristas@gmail.com>
Michal Rostecki <mrostecki@suse.de> Michal Rostecki <mrostecki@suse.de>
Michalis Kargakis <michaliskargakis@gmail.com> Michalis Kargakis <michaliskargakis@gmail.com>
@ -1556,6 +1636,7 @@ Michel Lespinasse <walken@google.com>
Mickael Kerjean <mickael.kerjean@gmail.com> Mickael Kerjean <mickael.kerjean@gmail.com>
Mickey Reiss <mickeyreiss@gmail.com> Mickey Reiss <mickeyreiss@gmail.com>
Miek Gieben <miek@miek.nl> <remigius.gieben@gmail.com> Miek Gieben <miek@miek.nl> <remigius.gieben@gmail.com>
Miguel Acero <acero@google.com>
Miguel Mendez <stxmendez@gmail.com> Miguel Mendez <stxmendez@gmail.com>
Miguel Molina <hi@mvader.me> Miguel Molina <hi@mvader.me>
Mihai Borobocea <MihaiBorobocea@gmail.com> Mihai Borobocea <MihaiBorobocea@gmail.com>
@ -1582,6 +1663,7 @@ Mikio Hara <mikioh.mikioh@gmail.com>
Mikkel Krautz <mikkel@krautz.dk> <krautz@gmail.com> Mikkel Krautz <mikkel@krautz.dk> <krautz@gmail.com>
Mikołaj Baranowski <mikolajb@gmail.com> Mikołaj Baranowski <mikolajb@gmail.com>
Milan Knezevic <milan.knezevic@mips.com> Milan Knezevic <milan.knezevic@mips.com>
Milan Patel <bicelot3@gmail.com>
Milutin Jovanović <jovanovic.milutin@gmail.com> Milutin Jovanović <jovanovic.milutin@gmail.com>
MinJae Kwon <mingrammer@gmail.com> MinJae Kwon <mingrammer@gmail.com>
Miquel Sabaté Solà <mikisabate@gmail.com> Miquel Sabaté Solà <mikisabate@gmail.com>
@ -1603,8 +1685,10 @@ Mrunal Patel <mrunalp@gmail.com>
Muhammad Falak R Wani <falakreyaz@gmail.com> Muhammad Falak R Wani <falakreyaz@gmail.com>
Muhammed Uluyol <uluyol0@gmail.com> Muhammed Uluyol <uluyol0@gmail.com>
Muir Manders <muir@mnd.rs> Muir Manders <muir@mnd.rs>
Mukesh Sharma <sharma.mukesh439@gmail.com>
Mura Li <mura_li@castech.com.tw> Mura Li <mura_li@castech.com.tw>
Mykhailo Lesyk <mikhail@lesyk.org> Mykhailo Lesyk <mikhail@lesyk.org>
Naman Aggarwal <aggarwal.nam@gmail.com>
Nan Deng <monnand@gmail.com> Nan Deng <monnand@gmail.com>
Nao Yonashiro <owan.orisano@gmail.com> Nao Yonashiro <owan.orisano@gmail.com>
Naoki Kanatani <k12naoki@gmail.com> Naoki Kanatani <k12naoki@gmail.com>
@ -1612,6 +1696,7 @@ Nate Wilkinson <nathanwilk7@gmail.com>
Nathan Cantelmo <n.cantelmo@gmail.com> Nathan Cantelmo <n.cantelmo@gmail.com>
Nathan Caza <mastercactapus@gmail.com> Nathan Caza <mastercactapus@gmail.com>
Nathan Dias <nathan.dias@orijtech.com> Nathan Dias <nathan.dias@orijtech.com>
Nathan Fiscaletti <nathan.fiscaletti@vrazo.com>
Nathan Humphreys <nkhumphreys@gmail.com> Nathan Humphreys <nkhumphreys@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>
@ -1621,6 +1706,7 @@ Nathan Youngman <git@nathany.com>
Nathan(yinian) Hu <nathanhu@google.com> Nathan(yinian) Hu <nathanhu@google.com>
Nathaniel Cook <nvcook42@gmail.com> Nathaniel Cook <nvcook42@gmail.com>
Naveen Kumar Sangi <naveenkumarsangi@protonmail.com> Naveen Kumar Sangi <naveenkumarsangi@protonmail.com>
Neeilan Selvalingam <neeilan96@gmail.com>
Neelesh Chandola <neelesh.c98@gmail.com> Neelesh Chandola <neelesh.c98@gmail.com>
Neil Lyons <nwjlyons@googlemail.com> Neil Lyons <nwjlyons@googlemail.com>
Neuman Vong <neuman.vong@gmail.com> Neuman Vong <neuman.vong@gmail.com>
@ -1661,17 +1747,20 @@ Nikita Vanyasin <nikita.vanyasin@gmail.com>
Niklas Schnelle <niklas.schnelle@gmail.com> Niklas Schnelle <niklas.schnelle@gmail.com>
Niko Dziemba <niko@dziemba.com> Niko Dziemba <niko@dziemba.com>
Nikolay Turpitko <nikolay@turpitko.com> Nikolay Turpitko <nikolay@turpitko.com>
Nikson Kanti Paul <nikson.sust@gmail.com>
Nils Larsgård <nilsmagnus@gmail.com> Nils Larsgård <nilsmagnus@gmail.com>
Nir Soffer <nirsof@gmail.com> Nir Soffer <nirsof@gmail.com>
Niranjan Godbole <niranjan8192@gmail.com> Niranjan Godbole <niranjan8192@gmail.com>
Nishanth Shanmugham <nishanth.gerrard@gmail.com> Nishanth Shanmugham <nishanth.gerrard@gmail.com>
Noah Campbell <noahcampbell@gmail.com> Noah Campbell <noahcampbell@gmail.com>
Noah Goldman <noahg34@gmail.com>
Noble Johnson <noblepoly@gmail.com> Noble Johnson <noblepoly@gmail.com>
Nodir Turakulov <nodir@google.com> Nodir Turakulov <nodir@google.com>
Noel Georgi <git@frezbo.com> Noel Georgi <git@frezbo.com>
Norberto Lopes <nlopes.ml@gmail.com> Norberto Lopes <nlopes.ml@gmail.com>
Norman B. Lancaster <qbradq@gmail.com> Norman B. Lancaster <qbradq@gmail.com>
Nuno Cruces <ncruces@users.noreply.github.com> Nuno Cruces <ncruces@users.noreply.github.com>
Obeyda Djeffal <djefobey@gmail.com>
Odin Ugedal <odin@ugedal.com> Odin Ugedal <odin@ugedal.com>
Oleg Bulatov <dmage@yandex-team.ru> Oleg Bulatov <dmage@yandex-team.ru>
Oleg Vakheta <helginet@gmail.com> Oleg Vakheta <helginet@gmail.com>
@ -1689,6 +1778,7 @@ Omar Jarjur <ojarjur@google.com>
Oryan Moshe <iamoryanmoshe@gmail.com> Oryan Moshe <iamoryanmoshe@gmail.com>
Osamu TONOMORI <osamingo@gmail.com> Osamu TONOMORI <osamingo@gmail.com>
Özgür Kesim <oec-go@kesim.org> Özgür Kesim <oec-go@kesim.org>
Pablo Caderno <kaderno@gmail.com>
Pablo Lalloni <plalloni@gmail.com> Pablo Lalloni <plalloni@gmail.com>
Pablo Rozas Larraondo <pablo.larraondo@anu.edu.au> Pablo Rozas Larraondo <pablo.larraondo@anu.edu.au>
Pablo Santiago Blum de Aguiar <scorphus@gmail.com> Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
@ -1702,6 +1792,8 @@ Parker Moore <parkrmoore@gmail.com>
Parminder Singh <parmsingh101@gmail.com> Parminder Singh <parmsingh101@gmail.com>
Pascal Dierich <pascal@pascaldierich.com> Pascal Dierich <pascal@pascaldierich.com>
Pascal S. de Kloe <pascal@quies.net> Pascal S. de Kloe <pascal@quies.net>
Paschalis Tsilias <paschalis.tsilias@gmail.com>
Pasi Tähkäpää <pasi.tahkapaa@gmail.com>
Pat Moroney <pat@pat.email> Pat Moroney <pat@pat.email>
Patrick Barker <barkerp@vmware.com> Patrick Barker <barkerp@vmware.com>
Patrick Crosby <patrick@stathat.com> Patrick Crosby <patrick@stathat.com>
@ -1718,6 +1810,7 @@ Paul A Querna <paul.querna@gmail.com>
Paul Borman <borman@google.com> Paul Borman <borman@google.com>
Paul Boyd <boyd.paul2@gmail.com> Paul Boyd <boyd.paul2@gmail.com>
Paul Chang <paulchang@google.com> Paul Chang <paulchang@google.com>
Paul D. Weber <x0bdev@gmail.com>
Paul Hammond <paul@paulhammond.org> Paul Hammond <paul@paulhammond.org>
Paul Hankin <paulhankin@google.com> Paul Hankin <paulhankin@google.com>
Paul Jolly <paul@myitcv.org.uk> Paul Jolly <paul@myitcv.org.uk>
@ -1743,8 +1836,10 @@ Pavel Zinovkin <pavel.zinovkin@gmail.com>
Pavlo Sumkin <ymkins@gmail.com> Pavlo Sumkin <ymkins@gmail.com>
Pawel Knap <pawelknap88@gmail.com> Pawel Knap <pawelknap88@gmail.com>
Pawel Szczur <filemon@google.com> Pawel Szczur <filemon@google.com>
Pei Xian Chee <luciolas1991@gmail.com>
Percy Wegmann <ox.to.a.cart@gmail.com> Percy Wegmann <ox.to.a.cart@gmail.com>
Perry Abbott <perry.j.abbott@gmail.com> Perry Abbott <perry.j.abbott@gmail.com>
Petar Dambovaliev <petar.atanasov.1987@gmail.com>
Petar Maymounkov <petarm@gmail.com> Petar Maymounkov <petarm@gmail.com>
Peter Armitage <peter.armitage@gmail.com> Peter Armitage <peter.armitage@gmail.com>
Peter Bourgon <peter@bourgon.org> Peter Bourgon <peter@bourgon.org>
@ -1781,6 +1876,7 @@ Philip Hofer <phofer@umich.edu>
Philip K. Warren <pkwarren@gmail.com> Philip K. Warren <pkwarren@gmail.com>
Philip Nelson <me@pnelson.ca> Philip Nelson <me@pnelson.ca>
Philipp Stephani <phst@google.com> Philipp Stephani <phst@google.com>
Pierre Carru <pierre.carru@eshard.com>
Pierre Durand <pierredurand@gmail.com> Pierre Durand <pierredurand@gmail.com>
Pierre Prinetti <pierreprinetti@gmail.com> Pierre Prinetti <pierreprinetti@gmail.com>
Pierre Roullon <pierre.roullon@gmail.com> Pierre Roullon <pierre.roullon@gmail.com>
@ -1789,11 +1885,14 @@ Pieter Droogendijk <pieter@binky.org.uk>
Pietro Gagliardi <pietro10@mac.com> Pietro Gagliardi <pietro10@mac.com>
Piyush Mishra <piyush@codeitout.com> Piyush Mishra <piyush@codeitout.com>
Plekhanov Maxim <kishtatix@gmail.com> Plekhanov Maxim <kishtatix@gmail.com>
Polina Osadcha <polliosa@google.com>
Pontus Leitzler <leitzler@gmail.com> Pontus Leitzler <leitzler@gmail.com>
Povilas Versockas <p.versockas@gmail.com>
Prasanga Siripala <pj@pjebs.com.au> Prasanga Siripala <pj@pjebs.com.au>
Prasanna Swaminathan <prasanna@mediamath.com> Prasanna Swaminathan <prasanna@mediamath.com>
Prashant Agrawal <prashant.a.vjti@gmail.com> Prashant Agrawal <prashant.a.vjti@gmail.com>
Prashant Varanasi <prashant@prashantv.com> Prashant Varanasi <prashant@prashantv.com>
Praveen Kumar <praveen+git@kumar.in>
Pravendra Singh <hackpravj@gmail.com> Pravendra Singh <hackpravj@gmail.com>
Preetam Jinka <pj@preet.am> Preetam Jinka <pj@preet.am>
Pure White <wu.purewhite@gmail.com> Pure White <wu.purewhite@gmail.com>
@ -1804,6 +1903,7 @@ Quan Yong Zhai <qyzhai@gmail.com>
Quentin Perez <qperez@ocs.online.net> Quentin Perez <qperez@ocs.online.net>
Quentin Renard <contact@asticode.com> Quentin Renard <contact@asticode.com>
Quentin Smith <quentin@golang.org> Quentin Smith <quentin@golang.org>
Quey-Liang Kao <s101062801@m101.nthu.edu.tw>
Quinn Slack <sqs@sourcegraph.com> Quinn Slack <sqs@sourcegraph.com>
Quinten Yearsley <qyearsley@chromium.org> Quinten Yearsley <qyearsley@chromium.org>
Quoc-Viet Nguyen <afelion@gmail.com> Quoc-Viet Nguyen <afelion@gmail.com>
@ -1831,6 +1931,7 @@ Reilly Watson <reillywatson@gmail.com>
Reinaldo de Souza Jr <juniorz@gmail.com> Reinaldo de Souza Jr <juniorz@gmail.com>
Remi Gillig <remigillig@gmail.com> Remi Gillig <remigillig@gmail.com>
Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com> Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
Ren Ogaki <re.yuz77777@gmail.com>
Rens Rikkerink <Ikkerens@users.noreply.github.com> Rens Rikkerink <Ikkerens@users.noreply.github.com>
Rhys Hiltner <rhys@justin.tv> Rhys Hiltner <rhys@justin.tv>
Ricardo Padilha <ricardospadilha@gmail.com> Ricardo Padilha <ricardospadilha@gmail.com>
@ -1842,6 +1943,8 @@ Richard Eric Gavaletz <gavaletz@gmail.com>
Richard Gibson <richard.gibson@gmail.com> Richard Gibson <richard.gibson@gmail.com>
Richard Miller <miller.research@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>
Richard Ulmer <codesoap@mailbox.org>
Richard Wilkes <wilkes@me.com>
Rick Arnold <rickarnoldjr@gmail.com> Rick Arnold <rickarnoldjr@gmail.com>
Rick Hudson <rlh@golang.org> Rick Hudson <rlh@golang.org>
Rick Sayre <whorfin@gmail.com> Rick Sayre <whorfin@gmail.com>
@ -1860,6 +1963,7 @@ Robert Figueiredo <robfig@gmail.com>
Robert Griesemer <gri@golang.org> Robert Griesemer <gri@golang.org>
Robert Hencke <robert.hencke@gmail.com> Robert Hencke <robert.hencke@gmail.com>
Robert Iannucci <iannucci@google.com> Robert Iannucci <iannucci@google.com>
Robert Kuska <rkuska@gmail.com>
Robert Obryk <robryk@gmail.com> Robert Obryk <robryk@gmail.com>
Robert Sesek <rsesek@google.com> Robert Sesek <rsesek@google.com>
Robert Snedegar <roberts@google.com> Robert Snedegar <roberts@google.com>
@ -1878,6 +1982,7 @@ Roger Pau Monné <royger@gmail.com>
Roger Peppe <rogpeppe@gmail.com> Roger Peppe <rogpeppe@gmail.com>
Rohan Challa <rohan@golang.org> Rohan Challa <rohan@golang.org>
Rohan Verma <rohanverma2004@gmail.com> Rohan Verma <rohanverma2004@gmail.com>
Rohith Ravi <entombedvirus@gmail.com>
Roland Illig <roland.illig@gmx.de> Roland Illig <roland.illig@gmx.de>
Roland Shoemaker <rolandshoemaker@gmail.com> Roland Shoemaker <rolandshoemaker@gmail.com>
Romain Baugue <romain.baugue@elwinar.com> Romain Baugue <romain.baugue@elwinar.com>
@ -1887,6 +1992,7 @@ Roman Shchekin <mrqtros@gmail.com>
Ron Hashimoto <mail@h2so5.net> Ron Hashimoto <mail@h2so5.net>
Ron Minnich <rminnich@gmail.com> Ron Minnich <rminnich@gmail.com>
Ross Chater <rdchater@gmail.com> Ross Chater <rdchater@gmail.com>
Ross Kinsey <rossikinsey@gmail.com>
Ross Light <light@google.com> <rlight2@gmail.com> Ross Light <light@google.com> <rlight2@gmail.com>
Ross Smith II <ross@smithii.com> Ross Smith II <ross@smithii.com>
Rowan Marshall <rowanajmarshall@gmail.com> Rowan Marshall <rowanajmarshall@gmail.com>
@ -1921,6 +2027,8 @@ Sakeven Jiang <jc5930@sina.cn>
Salmān Aljammāz <s@0x65.net> Salmān Aljammāz <s@0x65.net>
Sam Arnold <sarnold64@bloomberg.net> Sam Arnold <sarnold64@bloomberg.net>
Sam Boyer <tech@samboyer.org> Sam Boyer <tech@samboyer.org>
Sam Chen <chenxsan@gmail.com>
Sam Cross <samgcdev@gmail.com>
Sam Ding <samding@ca.ibm.com> Sam Ding <samding@ca.ibm.com>
Sam Hug <samuel.b.hug@gmail.com> 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>
@ -1972,6 +2080,7 @@ Sergey 'SnakE' Gromov <snake.scaly@gmail.com>
Sergey Arseev <sergey.arseev@intel.com> Sergey Arseev <sergey.arseev@intel.com>
Sergey Dobrodey <sergey.dobrodey@synesis.ru> Sergey Dobrodey <sergey.dobrodey@synesis.ru>
Sergey Frolov <sfrolov@google.com> Sergey Frolov <sfrolov@google.com>
Sergey Glushchenko <gsserge@gmail.com>
Sergey Ivanov <ser1325@gmail.com> Sergey Ivanov <ser1325@gmail.com>
Sergey Lukjanov <me@slukjanov.name> Sergey Lukjanov <me@slukjanov.name>
Sergey Mishin <sergeymishine@gmail.com> Sergey Mishin <sergeymishine@gmail.com>
@ -1987,7 +2096,9 @@ Seth Vargo <sethvargo@gmail.com>
Shahar Kohanim <skohanim@gmail.com> Shahar Kohanim <skohanim@gmail.com>
Shamil Garatuev <garatuev@gmail.com> Shamil Garatuev <garatuev@gmail.com>
Shane Hansen <shanemhansen@gmail.com> Shane Hansen <shanemhansen@gmail.com>
Shang Jian Ding <sding3@ncsu.edu>
Shaozhen Ding <dsz0111@gmail.com> Shaozhen Ding <dsz0111@gmail.com>
Shaquille Wyan Que <shaqqywyan@gmail.com>
Shaun Dunning <shaun.dunning@uservoice.com> Shaun Dunning <shaun.dunning@uservoice.com>
Shawn Elliott <selliott@microsoft.com> Shawn Elliott <selliott@microsoft.com>
Shawn Ledbetter <sledbetter@google.com> Shawn Ledbetter <sledbetter@google.com>
@ -2008,6 +2119,7 @@ Shubham Sharma <shubham.sha12@gmail.com>
Shun Fan <sfan@google.com> Shun Fan <sfan@google.com>
Silvan Jegen <s.jegen@gmail.com> Silvan Jegen <s.jegen@gmail.com>
Simarpreet Singh <simar@linux.com> Simarpreet Singh <simar@linux.com>
Simon Drake <simondrake1990@gmail.com>
Simon Ferquel <simon.ferquel@docker.com> Simon Ferquel <simon.ferquel@docker.com>
Simon Jefford <simon.jefford@gmail.com> Simon Jefford <simon.jefford@gmail.com>
Simon Rawet <simon@rawet.se> Simon Rawet <simon@rawet.se>
@ -2018,6 +2130,8 @@ Sina Siadat <siadat@gmail.com>
Sjoerd Siebinga <sjoerd.siebinga@gmail.com> Sjoerd Siebinga <sjoerd.siebinga@gmail.com>
Sokolov Yura <funny.falcon@gmail.com> Sokolov Yura <funny.falcon@gmail.com>
Song Gao <song@gao.io> Song Gao <song@gao.io>
Soojin Nam <jsunam@gmail.com>
Søren L. Hansen <soren@linux2go.dk>
Spencer Kocot <spencerkocot@gmail.com> Spencer Kocot <spencerkocot@gmail.com>
Spencer Nelson <s@spenczar.com> Spencer Nelson <s@spenczar.com>
Spencer Tung <spencertung@google.com> Spencer Tung <spencertung@google.com>
@ -2074,6 +2188,7 @@ Taavi Kivisik <taavi.kivisik@gmail.com>
Tad Fisher <tadfisher@gmail.com> Tad Fisher <tadfisher@gmail.com>
Tad Glines <tad.glines@gmail.com> Tad Glines <tad.glines@gmail.com>
Tadas Valiukas <tadovas@gmail.com> Tadas Valiukas <tadovas@gmail.com>
Tadeo Kondrak <me@tadeo.ca>
Taesu Pyo <pyotaesu@gmail.com> Taesu Pyo <pyotaesu@gmail.com>
Tai Le <letientai299@gmail.com> Tai Le <letientai299@gmail.com>
Taj Khattra <taj.khattra@gmail.com> Taj Khattra <taj.khattra@gmail.com>
@ -2083,6 +2198,7 @@ Takeshi YAMANASHI <9.nashi@gmail.com>
Takuto Ikuta <tikuta@google.com> Takuto Ikuta <tikuta@google.com>
Takuya Ueda <uedatakuya@gmail.com> Takuya Ueda <uedatakuya@gmail.com>
Tal Shprecher <tshprecher@gmail.com> Tal Shprecher <tshprecher@gmail.com>
Tamás Gulácsi <tgulacsi78@gmail.com>
Tamir Duberstein <tamird@gmail.com> Tamir Duberstein <tamird@gmail.com>
Tao Qingyun <qingyunha@gmail.com> Tao Qingyun <qingyunha@gmail.com>
Tao Shen <shentaoskyking@gmail.com> Tao Shen <shentaoskyking@gmail.com>
@ -2102,6 +2218,7 @@ Tetsuo Kiso <tetsuokiso9@gmail.com>
Than McIntosh <thanm@google.com> Than McIntosh <thanm@google.com>
Thanabodee Charoenpiriyakij <wingyminus@gmail.com> Thanabodee Charoenpiriyakij <wingyminus@gmail.com>
Thanatat Tamtan <acoshift@gmail.com> Thanatat Tamtan <acoshift@gmail.com>
The Hatsune Daishi <nao20010128@gmail.com>
Thiago Avelino <t@avelino.xxx> Thiago Avelino <t@avelino.xxx>
Thiago Fransosi Farina <thiago.farina@gmail.com> <tfarina@chromium.org> Thiago Fransosi Farina <thiago.farina@gmail.com> <tfarina@chromium.org>
Thomas Alan Copeland <talan.copeland@gmail.com> Thomas Alan Copeland <talan.copeland@gmail.com>
@ -2128,9 +2245,11 @@ Tim Ebringer <tim.ebringer@gmail.com>
Tim Heckman <t@heckman.io> Tim Heckman <t@heckman.io>
Tim Henderson <tim.tadh@gmail.com> Tim Henderson <tim.tadh@gmail.com>
Tim Hockin <thockin@google.com> Tim Hockin <thockin@google.com>
Tim Möhlmann <muhlemmer@gmail.com>
Tim Swast <swast@google.com> Tim Swast <swast@google.com>
Tim Wright <tenortim@gmail.com> Tim Wright <tenortim@gmail.com>
Tim Xu <xiaoxubeii@gmail.com> Tim Xu <xiaoxubeii@gmail.com>
Timmy Douglas <timmyd983@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> Timothy Studd <tim@timstudd.com>
@ -2149,6 +2268,7 @@ Tom Lanyon <tomlanyon@google.com>
Tom Levy <tomlevy93@gmail.com> Tom Levy <tomlevy93@gmail.com>
Tom Limoncelli <tal@whatexit.org> Tom Limoncelli <tal@whatexit.org>
Tom Linford <tomlinford@gmail.com> Tom Linford <tomlinford@gmail.com>
Tom Parkin <tom.parkin@gmail.com>
Tom Payne <twpayne@gmail.com> Tom Payne <twpayne@gmail.com>
Tom Szymanski <tgs@google.com> Tom Szymanski <tgs@google.com>
Tom Thorogood <me+google@tomthorogood.co.uk> Tom Thorogood <me+google@tomthorogood.co.uk>
@ -2162,6 +2282,7 @@ Tony Reix <tony.reix@bull.net>
Tony Walker <walkert.uk@gmail.com> Tony Walker <walkert.uk@gmail.com>
Tooru Takahashi <tooru.takahashi134@gmail.com> Tooru Takahashi <tooru.takahashi134@gmail.com>
Tor Andersson <tor.andersson@gmail.com> Tor Andersson <tor.andersson@gmail.com>
Torben Schinke <torben.schinke@neotos.de>
Tormod Erevik Lea <tormodlea@gmail.com> Tormod Erevik Lea <tormodlea@gmail.com>
Toshihiro Shiino <shiino.toshihiro@gmail.com> Toshihiro Shiino <shiino.toshihiro@gmail.com>
Toshiki Shima <hayabusa1419@gmail.com> Toshiki Shima <hayabusa1419@gmail.com>
@ -2178,12 +2299,15 @@ Tristan Ooohry <ooohry@gmail.com>
Tristan Rice <rice@fn.lc> Tristan Rice <rice@fn.lc>
Troels Thomsen <troels@thomsen.io> Troels Thomsen <troels@thomsen.io>
Trung Nguyen <trung.n.k@gmail.com> Trung Nguyen <trung.n.k@gmail.com>
Tsuji Daishiro <dram.dt.shonan@gmail.com>
Tudor Golubenco <tudor.g@gmail.com> Tudor Golubenco <tudor.g@gmail.com>
Tugdual Saunier <tugdual.saunier@gmail.com> Tugdual Saunier <tugdual.saunier@gmail.com>
Tuo Shan <sturbo89@gmail.com> <shantuo@google.com> Tuo Shan <sturbo89@gmail.com> <shantuo@google.com>
Tyler Bui-Palsulich <tpalsulich@google.com> Tyler Bui-Palsulich <tpalsulich@google.com>
Tyler Bunnell <tylerbunnell@gmail.com> Tyler Bunnell <tylerbunnell@gmail.com>
Tyler Treat <ttreat31@gmail.com> Tyler Treat <ttreat31@gmail.com>
Tyson Andre <tysonandre775@gmail.com>
Tzach Shabtay <tzachshabtay@gmail.com>
Tzu-Jung Lee <roylee17@currant.com> Tzu-Jung Lee <roylee17@currant.com>
Udalov Max <re.udalov@gmail.com> Udalov Max <re.udalov@gmail.com>
Ugorji Nwoke <ugorji@gmail.com> Ugorji Nwoke <ugorji@gmail.com>
@ -2217,6 +2341,7 @@ Visweswara R <r.visweswara@gmail.com>
Vitaly Zdanevich <zdanevich.vitaly@ya.ru> Vitaly Zdanevich <zdanevich.vitaly@ya.ru>
Vitor De Mario <vitordemario@gmail.com> Vitor De Mario <vitordemario@gmail.com>
Vivek Sekhar <vsekhar@google.com> Vivek Sekhar <vsekhar@google.com>
Vivian Liang <vliang88@gmail.com>
Vlad Krasnov <vlad@cloudflare.com> Vlad Krasnov <vlad@cloudflare.com>
Vladimir Evgrafov <evgrafov.vladimir@gmail.com> Vladimir Evgrafov <evgrafov.vladimir@gmail.com>
Vladimir Kovpak <cn007b@gmail.com> Vladimir Kovpak <cn007b@gmail.com>
@ -2231,6 +2356,7 @@ Volodymyr Paprotski <vpaprots@ca.ibm.com>
W. Trevor King <wking@tremily.us> W. Trevor King <wking@tremily.us>
Wade Simmons <wade@wades.im> Wade Simmons <wade@wades.im>
Wagner Riffel <wgrriffel@gmail.com> Wagner Riffel <wgrriffel@gmail.com>
Walt Della <walt@javins.net>
Walter Poupore <wpoupore@google.com> Walter Poupore <wpoupore@google.com>
Wander Lairson Costa <wcosta@mozilla.com> Wander Lairson Costa <wcosta@mozilla.com>
Wang Xuerui <git@xen0n.name> Wang Xuerui <git@xen0n.name>
@ -2274,12 +2400,15 @@ Xudong Zheng <7pkvm5aw@slicealias.com>
Xuyang Kang <xuyangkang@gmail.com> Xuyang Kang <xuyangkang@gmail.com>
Yamagishi Kazutoshi <ykzts@desire.sh> Yamagishi Kazutoshi <ykzts@desire.sh>
Yan Zou <yzou@google.com> Yan Zou <yzou@google.com>
Yang Hau <vulxj0j8j8@gmail.com>
Yang Tian <linuxty@gmail.com> Yang Tian <linuxty@gmail.com>
Yann Hodique <yhodique@google.com> Yann Hodique <yhodique@google.com>
Yann Kerhervé <yann.kerherve@gmail.com> Yann Kerhervé <yann.kerherve@gmail.com>
Yann Salaün <yannsalaun1@gmail.com> Yann Salaün <yannsalaun1@gmail.com>
Yannic Bonenberger <contact@yannic-bonenberger.com>
Yao Zhang <lunaria21@gmail.com> Yao Zhang <lunaria21@gmail.com>
Yaron de Leeuw <jarondl@google.com> Yaron de Leeuw <jarondl@google.com>
Yaroslav Vorobiov <yar.vorobiov@gmail.com>
Yasha Bubnov <girokompass@gmail.com> Yasha Bubnov <girokompass@gmail.com>
Yasser Abdolmaleki <yasser@yasser.ca> Yasser Abdolmaleki <yasser@yasser.ca>
Yasuharu Goto <matope.ono@gmail.com> Yasuharu Goto <matope.ono@gmail.com>
@ -2298,6 +2427,7 @@ Yoshiyuki Mineo <yoshiyuki.mineo@gmail.com>
Yosuke Akatsuka <yosuke.akatsuka@gmail.com> Yosuke Akatsuka <yosuke.akatsuka@gmail.com>
Yu Heng Zhang <annita.zhang@cn.ibm.com> Yu Heng Zhang <annita.zhang@cn.ibm.com>
Yu Xuan Zhang <zyxsh@cn.ibm.com> Yu Xuan Zhang <zyxsh@cn.ibm.com>
Yuichi Kishimoto <yk2220s@gmail.com>
Yuichi Nishiwaki <yuichi.nishiwaki@gmail.com> Yuichi Nishiwaki <yuichi.nishiwaki@gmail.com>
Yuji Yaginuma <yuuji.yaginuma@gmail.com> Yuji Yaginuma <yuuji.yaginuma@gmail.com>
Yuki OKUSHI <huyuumi.dev@gmail.com> Yuki OKUSHI <huyuumi.dev@gmail.com>
@ -2318,6 +2448,7 @@ Zak <zrjknill@gmail.com>
Zakatell Kanda <hi@zkanda.io> Zakatell Kanda <hi@zkanda.io>
Zellyn Hunter <zellyn@squareup.com> <zellyn@gmail.com> Zellyn Hunter <zellyn@squareup.com> <zellyn@gmail.com>
Zev Goldstein <zev.goldstein@gmail.com> Zev Goldstein <zev.goldstein@gmail.com>
Zhang Boyang <zhangboyang.id@gmail.com>
Zheng Dayu <davidzheng23@gmail.com> Zheng Dayu <davidzheng23@gmail.com>
Zheng Xu <zheng.xu@arm.com> Zheng Xu <zheng.xu@arm.com>
Zhengyu He <hzy@google.com> Zhengyu He <hzy@google.com>

View file

@ -456,3 +456,4 @@ pkg syscall (freebsd-arm-cgo), type Statfs_t struct, Mntonname [88]int8
pkg text/scanner, const GoTokens = 1012 pkg text/scanner, const GoTokens = 1012
pkg unicode, const Version = "10.0.0" pkg unicode, const Version = "10.0.0"
pkg unicode, const Version = "11.0.0" pkg unicode, const Version = "11.0.0"
pkg unicode, const Version = "12.0.0"

View file

@ -112,8 +112,6 @@ pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI ideal-int pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI ideal-int
pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX = 14 pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX = 14
pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX ideal-int pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX ideal-int
pkg go/printer, const StdFormat = 16
pkg go/printer, const StdFormat Mode
pkg math/big, method (*Int) FillBytes([]uint8) []uint8 pkg math/big, method (*Int) FillBytes([]uint8) []uint8
pkg net, method (*Resolver) LookupIP(context.Context, string, string) ([]IP, error) pkg net, method (*Resolver) LookupIP(context.Context, string, string) ([]IP, error)
pkg net/url, method (*URL) EscapedFragment() string pkg net/url, method (*URL) EscapedFragment() string

View file

@ -0,0 +1,19 @@
pkg unicode, const Version = "13.0.0"
pkg unicode, var Chorasmian *RangeTable
pkg unicode, var Dives_Akuru *RangeTable
pkg unicode, var Khitan_Small_Script *RangeTable
pkg unicode, var Yezidi *RangeTable
pkg text/template/parse, const NodeComment = 20
pkg text/template/parse, const NodeComment NodeType
pkg text/template/parse, const ParseComments = 1
pkg text/template/parse, const ParseComments Mode
pkg text/template/parse, method (*CommentNode) Copy() Node
pkg text/template/parse, method (*CommentNode) String() string
pkg text/template/parse, method (CommentNode) Position() Pos
pkg text/template/parse, method (CommentNode) Type() NodeType
pkg text/template/parse, type CommentNode struct
pkg text/template/parse, type CommentNode struct, Text string
pkg text/template/parse, type CommentNode struct, embedded NodeType
pkg text/template/parse, type CommentNode struct, embedded Pos
pkg text/template/parse, type Mode uint
pkg text/template/parse, type Tree struct, Mode Mode

View file

@ -257,6 +257,7 @@ To use the <code>net/http</code> package, it must be imported:
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
<b>"net/http"</b> <b>"net/http"</b>
) )
</pre> </pre>

View file

@ -687,6 +687,13 @@ MOVQ g(CX), AX // Move g into AX.
MOVQ g_m(AX), BX // Move g.m into BX. MOVQ g_m(AX), BX // Move g.m into BX.
</pre> </pre>
<p>
Register <code>BP</code> is callee-save.
The assembler automatically inserts <code>BP</code> save/restore when frame size is larger than zero.
Using <code>BP</code> as a general purpose register is allowed,
however it can interfere with sampling-based profiling.
</p>
<h3 id="arm">ARM</h3> <h3 id="arm">ARM</h3>
<p> <p>

View file

@ -45,8 +45,8 @@ CLA (Contributor License Agreement).
</li> </li>
<li> <li>
<b>Step 2</b>: Configure authentication credentials for the Go Git repository. <b>Step 2</b>: Configure authentication credentials for the Go Git repository.
Visit <a href="https://go.googlesource.com/">go.googlesource.com</a>, click Visit <a href="https://go.googlesource.com">go.googlesource.com</a>, click
on the gear icon (top right), then on "Obtain password", and follow the "Generate Password" in the page's top right menu bar, and follow the
instructions. instructions.
</li> </li>
<li> <li>

View file

@ -609,6 +609,12 @@ Do not send CLs removing the interior tags from such phrases.
If a program needs to accept invalid numbers like the empty string, If a program needs to accept invalid numbers like the empty string,
consider wrapping the type with <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>. consider wrapping the type with <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>.
</p> </p>
<p><!-- CL 200237 -->
<a href="/pkg/encoding/json/#Unmarshal"><code>Unmarshal</code></a>
can now support map keys with string underlying type which implement
<a href="/pkg/encoding/#TextUnmarshaler"><code>encoding.TextUnmarshaler</code></a>.
</p>
</dd> </dd>
</dl><!-- encoding/json --> </dl><!-- encoding/json -->

View file

@ -14,13 +14,21 @@ Do not send CLs removing the interior tags from such phrases.
main ul li { margin: 0.5em 0; } main ul li { margin: 0.5em 0; }
</style> </style>
<h2 id="introduction">DRAFT RELEASE NOTES — Introduction to Go 1.15</h2> <h2 id="introduction">Introduction to Go 1.15</h2>
<p> <p>
<strong> The latest Go release, version 1.15, arrives six months after <a href="go1.14">Go 1.14</a>.
Go 1.15 is not yet released. These are work-in-progress Most of its changes are in the implementation of the toolchain, runtime, and libraries.
release notes. Go 1.15 is expected to be released in August 2020. As always, the release maintains the Go 1 <a href="/doc/go1compat.html">promise of compatibility</a>.
</strong> We expect almost all Go programs to continue to compile and run as before.
</p>
<p>
Go 1.15 includes <a href="#linker">substantial improvements to the linker</a>,
improves <a href="#runtime">allocation for small objects at high core counts</a>, and
deprecates <a href="#commonname">X.509 CommonName</a>.
<code>GOPROXY</code> now supports skipping proxies that return errors and
a new <a href="#time/tzdata">embedded tzdata package</a> has been added.
</p> </p>
<h2 id="language">Changes to the language</h2> <h2 id="language">Changes to the language</h2>
@ -94,6 +102,16 @@ Do not send CLs removing the interior tags from such phrases.
preemption. preemption.
</p> </p>
<h3 id="386">386</h3>
<p><!-- golang.org/issue/40255 -->
Go 1.15 is the last release to support x87-only floating-point
hardware (<code>GO386=387</code>). Future releases will require at
least SSE2 support on 386, raising Go's
minimum <code>GOARCH=386</code> requirement to the Intel Pentium 4
(released in 2000) or AMD Opteron/Athlon 64 (released in 2003).
</p>
<h2 id="tools">Tools</h2> <h2 id="tools">Tools</h2>
<h3 id="go-command">Go command</h3> <h3 id="go-command">Go command</h3>
@ -336,8 +354,13 @@ Do not send CLs removing the interior tags from such phrases.
</p> </p>
<p><!-- CL 207877 --> <p><!-- CL 207877 -->
TODO: <a href="https://golang.org/cl/207877">https://golang.org/cl/207877</a>: Revert -buildmode=pie to internal linking. The linker now defaults to internal linking mode
The linker defaults to internal linking mode for PIE on linux/amd64 and linux/arm64, which does require a C linker. for <code>-buildmode=pie</code> on
<code>linux/amd64</code> and <code>linux/arm64</code>, so these
configurations no longer require a C linker. External linking
mode (which was the default in Go 1.14 for
<code>-buildmode=pie</code>) can still be requested with
<code>-ldflags=-linkmode=external</code> flag.
</p> </p>
<h2 id="objdump">Objdump</h2> <h2 id="objdump">Objdump</h2>
@ -374,6 +397,23 @@ Do not send CLs removing the interior tags from such phrases.
documentation</a> for more information. documentation</a> for more information.
</p> </p>
<h3 id="commonname">X.509 CommonName deprecation</h3>
<p><!-- CL 231379 -->
The deprecated, legacy behavior of treating the <code>CommonName</code>
field on X.509 certificates as a host name when no Subject Alternative Names
are present is now disabled by default. It can be temporarily re-enabled by
adding the value <code>x509ignoreCN=0</code> to the <code>GODEBUG</code>
environment variable.
</p>
<p>
Note that if the <code>CommonName</code> is an invalid host name, it's always
ignored, regardless of <code>GODEBUG</code> settings. Invalid names include
those with any characters other than letters, digits, hyphens and underscores,
and those with empty labels or trailing dots.
</p>
<h3 id="minor_library_changes">Minor changes to the library</h3> <h3 id="minor_library_changes">Minor changes to the library</h3>
<p> <p>
@ -396,6 +436,19 @@ Do not send CLs removing the interior tags from such phrases.
</dd> </dd>
</dl><!-- bufio --> </dl><!-- bufio -->
<dl id="context"><dt><a href="/pkg/context/">context</a></dt>
<dd>
<p><!-- CL 223777 -->
Creating a derived <code>Context</code> using a nil parent is now explicitly
disallowed. Any attempt to do so with the
<a href="/pkg/context/#WithValue"><code>WithValue</code></a>,
<a href="/pkg/context/#WithDeadline"><code>WithDeadline</code></a>, or
<a href="/pkg/context/#WithCancel"><code>WithCancel</code></a> functions
will cause a panic.
</p>
</dd>
</dl><!-- context -->
<dl id="crypto"><dt><a href="/pkg/crypto/">crypto</a></dt> <dl id="crypto"><dt><a href="/pkg/crypto/">crypto</a></dt>
<dd> <dd>
<p><!-- CL 231417, CL 225460 --> <p><!-- CL 231417, CL 225460 -->
@ -495,6 +548,17 @@ Do not send CLs removing the interior tags from such phrases.
fields <code>OCSPResponse</code> and <code>SignedCertificateTimestamps</code> fields <code>OCSPResponse</code> and <code>SignedCertificateTimestamps</code>
are now repopulated on client-side resumed connections. are now repopulated on client-side resumed connections.
</p> </p>
<p><!-- CL 227840 -->
<a href="/pkg/crypto/tls/#Conn"><code>tls.Conn</code></a>
now returns an opaque error on permanently broken connections, wrapping
the temporary
<a href="/pkg/net/http/#Error"><code>net.Error</code></a>. To access the
original <code>net.Error</code>, use
<a href="/pkg/errors/#As"><code>errors.As</code></a> (or
<a href="/pkg/errors/#Unwrap"><code>errors.Unwrap</code></a>) instead of a
type assertion.
</p>
</dd> </dd>
</dl><!-- crypto/tls --> </dl><!-- crypto/tls -->
@ -511,15 +575,6 @@ Do not send CLs removing the interior tags from such phrases.
certificates with trailing dots. certificates with trailing dots.
</p> </p>
<p><!-- CL 231379 -->
The deprecated, legacy behavior of treating the <code>CommonName</code>
field as a hostname when no Subject Alternative Names are present is now
disabled by default. It can be temporarily re-enabled by adding the value
<code>x509ignoreCN=0</code> to the <code>GODEBUG</code> environment
variable. If the <code>CommonName</code> is an invalid hostname, it's
always ignored.
</p>
<p><!-- CL 217298 --> <p><!-- CL 217298 -->
The new <a href="/pkg/crypto/x509/#CreateRevocationList"><code>CreateRevocationList</code></a> The new <a href="/pkg/crypto/x509/#CreateRevocationList"><code>CreateRevocationList</code></a>
function and <a href="/pkg/crypto/x509/#RevocationList"><code>RevocationList</code></a> type function and <a href="/pkg/crypto/x509/#RevocationList"><code>RevocationList</code></a> type
@ -618,11 +673,6 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt> <dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt>
<dd> <dd>
<p><!-- CL 191783 -->
Decoding a JSON array into a slice no longer reuses any existing slice elements,
following the rules that the package documentation already stated.
</p>
<p><!-- CL 199837 --> <p><!-- CL 199837 -->
The package now has an internal limit to the maximum depth of The package now has an internal limit to the maximum depth of
nesting when decoding. This reduces the possibility that a nesting when decoding. This reduces the possibility that a
@ -635,8 +685,8 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt> <dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt>
<dd> <dd>
<p><!-- CL 221427 --> <p><!-- CL 221427 -->
When the flag package sees <code>-h</code> or <code>-help</code>, and When the <code>flag</code> package sees <code>-h</code> or <code>-help</code>,
those flags are not defined, it now prints a usage message. and those flags are not defined, it now prints a usage message.
If the <a href="/pkg/flag/#FlagSet"><code>FlagSet</code></a> was created with If the <a href="/pkg/flag/#FlagSet"><code>FlagSet</code></a> was created with
<a href="/pkg/flag/#ExitOnError"><code>ExitOnError</code></a>, <a href="/pkg/flag/#ExitOnError"><code>ExitOnError</code></a>,
<a href="/pkg/flag/#FlagSet.Parse"><code>FlagSet.Parse</code></a> would then <a href="/pkg/flag/#FlagSet.Parse"><code>FlagSet.Parse</code></a> would then
@ -656,15 +706,18 @@ Do not send CLs removing the interior tags from such phrases.
</dd> </dd>
</dl><!-- fmt --> </dl><!-- fmt -->
<dl id="go/printer"><dt><a href="/pkg/go/printer/">go/printer</a></dt> <dl id="go/format"><dt><a href="/pkg/go/format/">go/format</a></dt>
<dd> <dd>
<p><!-- CL 231461 --> <p><!-- golang.org/issue/37476, CL 231461, CL 240683 -->
The new <a href="/pkg/go/printer/#Mode"><code>Mode</code></a> The <a href="/pkg/go/format/#Source"><code>Source</code></a> and
value <a href="/pkg/go/printer/#StdFormat"><code>StdFormat</code></a> <a href="/pkg/go/format/#Node"><code>Node</code></a> functions
directs the printer to apply standard formatting changes while now canonicalize number literal prefixes and exponents as part
printing the output. of formatting Go source code. This matches the behavior of the
<a href="/pkg/cmd/gofmt/"><code>gofmt</code></a> command as it
was implemented <a href="/doc/go1.13#gofmt">since Go 1.13</a>.
</p>
</dd> </dd>
</dl><!-- go/printer --> </dl><!-- go/format -->
<dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt> <dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt>
<dd> <dd>
@ -698,6 +751,16 @@ Do not send CLs removing the interior tags from such phrases.
</dd> </dd>
</dl><!-- math/big --> </dl><!-- math/big -->
<dl id="math/cmplx"><dt><a href="/pkg/math/cmplx/">math/cmplx</a></dt>
<dd>
<p><!-- CL 220689 -->
The functions in this package were updated to conform to the C99 standard
(Annex G IEC 60559-compatible complex arithmetic) with respect to handling
of special arguments such as infinity, NaN and signed zero.
</p>
</dd>
</dl><!-- math/cmplx-->
<dl id="net"><dt><a href="/pkg/net/">net</a></dt> <dl id="net"><dt><a href="/pkg/net/">net</a></dt>
<dd> <dd>
<p><!-- CL 228645 --> <p><!-- CL 228645 -->
@ -868,9 +931,9 @@ Do not send CLs removing the interior tags from such phrases.
<dl id="pkg-runtime-pprof"><dt><a href="/pkg/runtime/pprof/">runtime/pprof</a></dt> <dl id="pkg-runtime-pprof"><dt><a href="/pkg/runtime/pprof/">runtime/pprof</a></dt>
<dd> <dd>
<p><!-- CL 189318 --> <p><!-- CL 189318 -->
The goroutine profile includes the profile labels associated with each goroutine The goroutine profile now includes the profile labels associated with each
at the time of profiling. This feature is not yet implemented for the profile goroutine at the time of profiling. This feature is not yet implemented for
reported with <code>debug=2</code>. the profile reported with <code>debug=2</code>.
</p> </p>
</dd> </dd>
</dl> </dl>
@ -901,6 +964,7 @@ Do not send CLs removing the interior tags from such phrases.
<a href="/pkg/sync/#Map.Delete"><code>Map.Delete</code></a> <a href="/pkg/sync/#Map.Delete"><code>Map.Delete</code></a>
is more efficient. is more efficient.
</p> </p>
</dd>
</dl><!-- sync --> </dl><!-- sync -->
<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt> <dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
@ -956,7 +1020,8 @@ Do not send CLs removing the interior tags from such phrases.
</p> </p>
<p><!-- CL 229085 --> <p><!-- CL 229085 -->
TODO: <a href="https://golang.org/cl/229085">https://golang.org/cl/229085</a>: reformat test chatty output <code>go</code> <code>test</code> <code>-v</code> now groups output by
test name, rather than printing the test name on each line.
</p> </p>
</dd> </dd>
</dl><!-- testing --> </dl><!-- testing -->

224
doc/go1.16.html Normal file
View file

@ -0,0 +1,224 @@
<!--{
"Title": "Go 1.16 Release Notes",
"Path": "/doc/go1.16"
}-->
<!--
NOTE: In this document and others in this directory, the convention is to
set fixed-width phrases with non-fixed-width spaces, as in
<code>hello</code> <code>world</code>.
Do not send CLs removing the interior tags from such phrases.
-->
<style>
main ul li { margin: 0.5em 0; }
</style>
<h2 id="introduction">DRAFT RELEASE NOTES — Introduction to Go 1.16</h2>
<p>
<strong>
Go 1.16 is not yet released. These are work-in-progress
release notes. Go 1.16 is expected to be released in February 2021.
</strong>
</p>
<h2 id="language">Changes to the language</h2>
<p>
TODO
</p>
<h2 id="ports">Ports</h2>
<p>
TODO
</p>
<h2 id="tools">Tools</h2>
<p>
TODO
</p>
<h3 id="go-command">Go command</h3>
<h4 id="modules">Modules</h4>
<p><!-- golang.org/issue/40276 -->
<code>go</code> <code>install</code> now accepts arguments with
version suffixes (for example, <code>go</code> <code>install</code>
<code>example.com/cmd@v1.0.0</code>). This causes <code>go</code>
<code>install</code> to build and install packages in module-aware mode,
ignoring the <code>go.mod</code> file in the current directory or any parent
directory, if there is one. This is useful for installing executables without
affecting the dependencies of the main module.<br>
TODO: write and link to section in golang.org/ref/mod<br>
TODO: write and link to blog post
</p>
<p><!-- golang.org/issue/24031 -->
<code>retract</code> directives may now be used in a <code>go.mod</code> file
to indicate that certain published versions of the module should not be used
by other modules. A module author may retract a version after a severe problem
is discovered or if the version was published unintentionally.<br>
TODO: write and link to section in golang.org/ref/mod<br>
TODO: write and link to tutorial or blog post
</p>
<p><!-- golang.org/issue/26603 -->
The <code>go</code> <code>mod</code> <code>vendor</code>
and <code>go</code> <code>mod</code> <code>tidy</code> subcommands now accept
the <code>-e</code> flag, which instructs them to proceed despite errors in
resolving missing packages.
</p>
<h4 id="go-test"><code>go</code> <code>test</code></h4>
<p><!-- golang.org/issue/29062 -->
When using <code>go</code> <code>test</code>, a test that
calls <code>os.Exit(0)</code> during execution of a test function
will now be considered to fail.
This will help catch cases in which a test calls code that calls
<code>os.Exit(0)</code> and thereby stops running all future tests.
If a <code>TestMain</code> function calls <code>os.Exit(0)</code>
that is still considered to be a passing test.
</p>
<h4 id="all-pattern">The <code>all</code> pattern</h4>
<p><!-- golang.org/cl/240623 -->
When the main module's <code>go.mod</code> file
declares <code>go</code> <code>1.16</code> or higher, the <code>all</code>
package pattern now matches only those packages that are transitively imported
by a package or test found in the main module. (Packages imported by <em>tests
of</em> packages imported by the main module are no longer included.) This is
the same set of packages retained
by <code>go</code> <code>mod</code> <code>vendor</code> since Go 1.11.
</p>
<h3 id="cgo">Cgo</h3>
<p> <!-- CL 252378 -->
The <a href="/cmd/cgo">cgo</a> tool will no longer try to translate
C struct bitfields into Go struct fields, even if their size can be
represented in Go. The order in which C bitfields appear in memory
is implementation dependent, so in some cases the cgo tool produced
results that were silently incorrect.
</p>
<p>
TODO
</p>
<h2 id="runtime">Runtime</h2>
<p>
TODO
</p>
<h2 id="compiler">Compiler</h2>
<p>
TODO
</p>
<h2 id="linker">Linker</h2>
<p>
This release includes additional improvements to the Go linker,
reducing linker resource usage (both time and memory) and improving
code robustness/maintainability. These changes form the second half
of a two-release project to
<a href="https://golang.org/s/better-linker">modernize the Go
linker</a>.
</p>
<p>
The linker changes in 1.16 extend the 1.15 improvements to all
supported architecture/OS combinations (the 1.15 performance improvements
were primarily focused on <code>ELF</code>-based OSes and
<code>amd64</code> architectures). For a representative set of
large Go programs, linking is 20-35% faster than 1.15 and requires
5-15% less memory on average for <code>linux/amd64</code>, with larger
improvements for other architectures and OSes.
</p>
<p>
TODO: update with final numbers later in the release.
</p>
<h2 id="library">Core library</h2>
<p>
TODO
</p>
<h3 id="net"><a href="/pkg/net/">net</a></h3>
<p><!-- CL 250357 -->
The case of I/O on a closed network connection, or I/O on a network
connection that is closed before any of the I/O completes, can now
be detected using the new <a href="/pkg/net/#ErrClosed">ErrClosed</a> error.
A typical use would be <code>errors.Is(err, net.ErrClosed)</code>.
In earlier releases the only way to reliably detect this case was to
match the string returned by the <code>Error</code> method
with <code>"use of closed network connection"</code>.
</p>
<h3 id="text/template/parse"><a href="/pkg/text/template/parse/">text/template/parse</a></h3>
<p><!-- CL 229398, golang.org/issue/34652 -->
A new <a href="/pkg/text/template/parse/#CommentNode"><code>CommentNode</code></a>
was added to the parse tree. The <a href="/pkg/text/template/parse/#Mode"><code>Mode</code></a>
field in the <code>parse.Tree</code> enables access to it.
</p>
<!-- text/template/parse -->
<h3 id="unicode"><a href="/pkg/unicode/">unicode</a></h3>
<p><!-- CL 248765 -->
The <a href="/pkg/unicode/"><code>unicode</code></a> package and associated
support throughout the system has been upgraded from Unicode 12.0.0 to
<a href="https://www.unicode.org/versions/Unicode13.0.0/">Unicode 13.0.0</a>,
which adds 5,930 new characters, including four new scripts, and 55 new emoji.
Unicode 13.0.0 also designates plane 3 (U+30000-U+3FFFF) as the tertiary
ideographic plane.
</p>
<h3 id="minor_library_changes">Minor changes to the library</h3>
<p>
As always, there are various minor changes and updates to the library,
made with the Go 1 <a href="/doc/go1compat">promise of compatibility</a>
in mind.
</p>
<p>
TODO
</p>
<dl id="net/http"><dt><a href="/pkg/net/http/">net/http</a></dt>
<dd>
<p><!-- CL 233637 -->
In the <a href="/pkg/net/http/"><code>net/http</code></a> package, the
behavior of <a href="/pkg/net/http/#StripPrefix"><code>StripPrefix</code></a>
has been changed to strip the prefix from the request URL's
<code>RawPath</code> field in addition to its <code>Path</code> field.
In past releases, only the <code>Path</code> field was trimmed, and so if the
request URL contained any escaped characters the URL would be modified to
have mismatched <code>Path</code> and <code>RawPath</code> fields.
In Go 1.16, <code>StripPrefix</code> trims both fields.
If there are escaped characters in the prefix part of the request URL the
handler serves a 404 instead of its previous behavior of invoking the
underlying handler with a mismatched <code>Path</code>/<code>RawPath</code> pair.
</p>
<p><!-- CL 252497 -->
The <a href="/pkg/net/http/"><code>net/http</code></a> package now rejects HTTP range requests
of the form <code>"Range": "bytes=--N"</code> where <code>"-N"</code> is a negative suffix length, for
example <code>"Range": "bytes=--2"</code>. It now replies with a <code>416 "Range Not Satisfiable"</code> response.
</p>
</dd>
</dl><!-- net/http -->

View file

@ -507,8 +507,8 @@ These default to the values of <code>$GOHOSTOS</code> and
<p> <p>
Choices for <code>$GOOS</code> are Choices for <code>$GOOS</code> are
<code>android</code>, <code>darwin</code> (macOS/iOS), <code>android</code>, <code>darwin</code>, <code>dragonfly</code>,
<code>dragonfly</code>, <code>freebsd</code>, <code>illumos</code>, <code>js</code>, <code>freebsd</code>, <code>illumos</code>, <code>ios</code>, <code>js</code>,
<code>linux</code>, <code>netbsd</code>, <code>openbsd</code>, <code>linux</code>, <code>netbsd</code>, <code>openbsd</code>,
<code>plan9</code>, <code>solaris</code> and <code>windows</code>. <code>plan9</code>, <code>solaris</code> and <code>windows</code>.
</p> </p>
@ -567,6 +567,9 @@ The valid combinations of <code>$GOOS</code> and <code>$GOARCH</code> are:
<td></td><td><code>illumos</code></td> <td><code>amd64</code></td> <td></td><td><code>illumos</code></td> <td><code>amd64</code></td>
</tr> </tr>
<tr> <tr>
<td></td><td><code>ios</code></td> <td><code>arm64</code></td>
</tr>
<tr>
<td></td><td><code>js</code></td> <td><code>wasm</code></td> <td></td><td><code>js</code></td> <td><code>wasm</code></td>
</tr> </tr>
<tr> <tr>
@ -600,6 +603,9 @@ The valid combinations of <code>$GOOS</code> and <code>$GOARCH</code> are:
<td></td><td><code>linux</code></td> <td><code>mips64le</code></td> <td></td><td><code>linux</code></td> <td><code>mips64le</code></td>
</tr> </tr>
<tr> <tr>
<td></td><td><code>linux</code></td> <td><code>riscv64</code></td>
</tr>
<tr>
<td></td><td><code>linux</code></td> <td><code>s390x</code></td> <td></td><td><code>linux</code></td> <td><code>s390x</code></td>
</tr> </tr>
<tr> <tr>

View file

@ -105,7 +105,7 @@ func test(tmpdir, file, want string) error {
// Canonicalize output. // Canonicalize output.
out = bytes.TrimRight(out, "\n") out = bytes.TrimRight(out, "\n")
out = bytes.Replace(out, []byte{'\n'}, []byte{' '}, -1) out = bytes.ReplaceAll(out, []byte{'\n'}, []byte{' '})
// Check the result. // Check the result.
match, err := regexp.Match(want, out) match, err := regexp.Match(want, out)

View file

@ -24,7 +24,7 @@ func test18146(t *testing.T) {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS) t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS)
} }

View file

@ -30,7 +30,7 @@ func TestCrossPackageTests(t *testing.T) {
switch runtime.GOOS { switch runtime.GOOS {
case "android": case "android":
t.Skip("Can't exec cmd/go subprocess on Android.") t.Skip("Can't exec cmd/go subprocess on Android.")
case "darwin": case "darwin", "ios":
switch runtime.GOARCH { switch runtime.GOARCH {
case "arm64": case "arm64":
t.Skip("Can't exec cmd/go subprocess on iOS.") t.Skip("Can't exec cmd/go subprocess on iOS.")

View file

@ -62,7 +62,7 @@ import (
func testSigaltstack(t *testing.T) { func testSigaltstack(t *testing.T) {
switch { switch {
case runtime.GOOS == "solaris", runtime.GOOS == "illumos", runtime.GOOS == "darwin" && runtime.GOARCH == "arm64": case runtime.GOOS == "solaris", runtime.GOOS == "illumos", (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64":
t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
} }

View file

@ -319,6 +319,7 @@ typedef enum {
// issue 4339 // issue 4339
// We've historically permitted #include <>, so test it here. Issue 29333. // We've historically permitted #include <>, so test it here. Issue 29333.
// Also see issue 41059.
#include <issue4339.h> #include <issue4339.h>
// issue 4417 // issue 4417
@ -901,6 +902,12 @@ typedef struct S32579 { unsigned char data[1]; } S32579;
// issue 38649 // issue 38649
// Test that #define'd type aliases work. // Test that #define'd type aliases work.
#define netbsd_gid unsigned int #define netbsd_gid unsigned int
// issue 40494
// Inconsistent handling of tagged enum and union types.
enum Enum40494 { X_40494 };
union Union40494 { int x; };
void issue40494(enum Enum40494 e, union Union40494* up) {}
*/ */
import "C" import "C"
@ -1769,7 +1776,7 @@ func test14838(t *testing.T) {
var sink C.int var sink C.int
func test17065(t *testing.T) { func test17065(t *testing.T) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
t.Skip("broken on darwin; issue 17065") t.Skip("broken on darwin; issue 17065")
} }
for i := range C.ii { for i := range C.ii {
@ -2204,3 +2211,10 @@ var issue38649 C.netbsd_gid = 42
// issue 39877 // issue 39877
var issue39877 *C.void = nil var issue39877 *C.void = nil
// issue 40494
// No runtime test; just make sure it compiles.
func Issue40494() {
C.issue40494(C.enum_Enum40494(C.X_40494), (*C.union_Union40494)(nil))
}

View file

@ -164,7 +164,7 @@ func Add(x int) {
} }
func testCthread(t *testing.T) { func testCthread(t *testing.T) {
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
t.Skip("the iOS exec wrapper is unable to properly handle the panic from Add") t.Skip("the iOS exec wrapper is unable to properly handle the panic from Add")
} }
sum.i = 0 sum.i = 0

View file

@ -118,9 +118,9 @@ func testMain(m *testing.M) int {
cc = append(cc, s[start:]) cc = append(cc, s[start:])
} }
if GOOS == "darwin" { if GOOS == "darwin" || GOOS == "ios" {
// For Darwin/ARM. // For Darwin/ARM.
// TODO(crawshaw): can we do better? // TODO: do we still need this?
cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
} }
if GOOS == "aix" { if GOOS == "aix" {
@ -133,7 +133,7 @@ func testMain(m *testing.M) int {
libbase = "gccgo_" + libgodir + "_fPIC" libbase = "gccgo_" + libgodir + "_fPIC"
} else { } else {
switch GOOS { switch GOOS {
case "darwin": case "darwin", "ios":
if GOARCH == "arm64" { if GOARCH == "arm64" {
libbase += "_shared" libbase += "_shared"
} }
@ -303,7 +303,7 @@ func TestInstall(t *testing.T) {
func TestEarlySignalHandler(t *testing.T) { func TestEarlySignalHandler(t *testing.T) {
switch GOOS { switch GOOS {
case "darwin": case "darwin", "ios":
switch GOARCH { switch GOARCH {
case "arm64": case "arm64":
t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
@ -384,7 +384,7 @@ func TestSignalForwarding(t *testing.T) {
expectSignal(t, err, syscall.SIGSEGV) expectSignal(t, err, syscall.SIGSEGV)
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
if runtime.GOOS != "darwin" { if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
// Test SIGPIPE forwarding // Test SIGPIPE forwarding
cmd = exec.Command(bin[0], append(bin[1:], "3")...) cmd = exec.Command(bin[0], append(bin[1:], "3")...)
@ -485,7 +485,7 @@ func TestSignalForwardingExternal(t *testing.T) {
// doesn't work on this platform. // doesn't work on this platform.
func checkSignalForwardingTest(t *testing.T) { func checkSignalForwardingTest(t *testing.T) {
switch GOOS { switch GOOS {
case "darwin": case "darwin", "ios":
switch GOARCH { switch GOARCH {
case "arm64": case "arm64":
t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
@ -603,7 +603,7 @@ func TestExtar(t *testing.T) {
if runtime.Compiler == "gccgo" { if runtime.Compiler == "gccgo" {
t.Skip("skipping -extar test when using gccgo") t.Skip("skipping -extar test when using gccgo")
} }
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
t.Skip("shell scripts are not executable on iOS hosts") t.Skip("shell scripts are not executable on iOS hosts")
} }
@ -645,7 +645,7 @@ func TestExtar(t *testing.T) {
func TestPIE(t *testing.T) { func TestPIE(t *testing.T) {
switch GOOS { switch GOOS {
case "windows", "darwin", "plan9": case "windows", "darwin", "ios", "plan9":
t.Skipf("skipping PIE test on %s", GOOS) t.Skipf("skipping PIE test on %s", GOOS)
} }
@ -738,7 +738,7 @@ func TestSIGPROF(t *testing.T) {
switch GOOS { switch GOOS {
case "windows", "plan9": case "windows", "plan9":
t.Skipf("skipping SIGPROF test on %s", GOOS) t.Skipf("skipping SIGPROF test on %s", GOOS)
case "darwin": case "darwin", "ios":
t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS) t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
} }
@ -841,7 +841,7 @@ func TestCompileWithoutShared(t *testing.T) {
expectSignal(t, err, syscall.SIGSEGV) expectSignal(t, err, syscall.SIGSEGV)
// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
if runtime.GOOS != "darwin" { if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
binArgs := append(cmdToRun(exe), "3") binArgs := append(cmdToRun(exe), "3")
t.Log(binArgs) t.Log(binArgs)
out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()

View file

@ -98,7 +98,7 @@ func testMain(m *testing.M) int {
} }
switch GOOS { switch GOOS {
case "darwin": case "darwin", "ios":
// 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"}...)
@ -107,7 +107,7 @@ func testMain(m *testing.M) int {
} }
libgodir := GOOS + "_" + GOARCH libgodir := GOOS + "_" + GOARCH
switch GOOS { switch GOOS {
case "darwin": case "darwin", "ios":
if GOARCH == "arm64" { if GOARCH == "arm64" {
libgodir += "_shared" libgodir += "_shared"
} }
@ -407,7 +407,7 @@ func TestUnexportedSymbols(t *testing.T) {
adbPush(t, libname) adbPush(t, libname)
linkFlags := "-Wl,--no-as-needed" linkFlags := "-Wl,--no-as-needed"
if GOOS == "darwin" { if GOOS == "darwin" || GOOS == "ios" {
linkFlags = "" linkFlags = ""
} }
@ -636,7 +636,7 @@ func copyFile(t *testing.T, dst, src string) {
func TestGo2C2Go(t *testing.T) { func TestGo2C2Go(t *testing.T) {
switch GOOS { switch GOOS {
case "darwin": case "darwin", "ios":
// Darwin shared libraries don't support the multiple // Darwin shared libraries don't support the multiple
// copies of the runtime package implied by this test. // copies of the runtime package implied by this test.
t.Skip("linking c-shared into Go programs not supported on Darwin; issue 29061") t.Skip("linking c-shared into Go programs not supported on Darwin; issue 29061")

View file

@ -0,0 +1,31 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// +build ignore
package main
// This file tests that we don't generate an incorrect field location
// for a bitfield that appears aligned.
/*
struct bitfields {
unsigned int B1 : 5;
unsigned int B2 : 1;
unsigned int B3 : 1;
unsigned int B4 : 1;
unsigned int Short1 : 16; // misaligned on 8 bit boundary
unsigned int B5 : 1;
unsigned int B6 : 1;
unsigned int B7 : 1;
unsigned int B8 : 1;
unsigned int B9 : 1;
unsigned int B10 : 3;
unsigned int Short2 : 16; // alignment is OK
unsigned int Short3 : 16; // alignment is OK
};
*/
import "C"
type bitfields C.struct_bitfields

View file

@ -4,6 +4,12 @@
package main package main
import (
"fmt"
"os"
"reflect"
)
// Test that the struct field in anonunion.go was promoted. // Test that the struct field in anonunion.go was promoted.
var v1 T var v1 T
var v2 = v1.L var v2 = v1.L
@ -23,4 +29,26 @@ var v7 = S{}
var _ = issue38649{X: 0} var _ = issue38649{X: 0}
func main() { func main() {
pass := true
// The Go translation of bitfields should not have any of the
// bitfield types. The order in which bitfields are laid out
// in memory is implementation defined, so we can't easily
// know how a bitfield should correspond to a Go type, even if
// it appears to be aligned correctly.
bitfieldType := reflect.TypeOf(bitfields{})
check := func(name string) {
_, ok := bitfieldType.FieldByName(name)
if ok {
fmt.Fprintf(os.Stderr, "found unexpected bitfields field %s\n", name)
pass = false
}
}
check("Short1")
check("Short2")
check("Short3")
if !pass {
os.Exit(1)
}
} }

View file

@ -19,6 +19,7 @@ import (
// import "C" block. Add more tests here. // import "C" block. Add more tests here.
var filePrefixes = []string{ var filePrefixes = []string{
"anonunion", "anonunion",
"bitfields",
"issue8478", "issue8478",
"fieldtypedef", "fieldtypedef",
"issue37479", "issue37479",

View file

@ -462,6 +462,7 @@ func TestTrivialExecutable(t *testing.T) {
run(t, "trivial executable", "../../bin/trivial") run(t, "trivial executable", "../../bin/trivial")
AssertIsLinkedTo(t, "../../bin/trivial", soname) AssertIsLinkedTo(t, "../../bin/trivial", soname)
AssertHasRPath(t, "../../bin/trivial", gorootInstallDir) AssertHasRPath(t, "../../bin/trivial", gorootInstallDir)
checkSize(t, "../../bin/trivial", 100000) // it is 19K on linux/amd64, 100K should be enough
} }
// Build a trivial program in PIE mode that links against the shared runtime and check it runs. // Build a trivial program in PIE mode that links against the shared runtime and check it runs.
@ -470,6 +471,18 @@ func TestTrivialExecutablePIE(t *testing.T) {
run(t, "trivial executable", "./trivial.pie") run(t, "trivial executable", "./trivial.pie")
AssertIsLinkedTo(t, "./trivial.pie", soname) AssertIsLinkedTo(t, "./trivial.pie", soname)
AssertHasRPath(t, "./trivial.pie", gorootInstallDir) AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
checkSize(t, "./trivial.pie", 100000) // it is 19K on linux/amd64, 100K should be enough
}
// Check that the file size does not exceed a limit.
func checkSize(t *testing.T, f string, limit int64) {
fi, err := os.Stat(f)
if err != nil {
t.Fatalf("stat failed: %v", err)
}
if sz := fi.Size(); sz > limit {
t.Errorf("file too large: got %d, want <= %d", sz, limit)
}
} }
// Build a division test program and check it runs. // Build a division test program and check it runs.

View file

@ -21,7 +21,7 @@ func requireTestSOSupported(t *testing.T) {
t.Helper() t.Helper()
switch runtime.GOARCH { switch runtime.GOARCH {
case "arm64": case "arm64":
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
t.Skip("No exec facility on iOS.") t.Skip("No exec facility on iOS.")
} }
case "ppc64": case "ppc64":
@ -74,7 +74,7 @@ func TestSO(t *testing.T) {
ext := "so" ext := "so"
args := append(gogccflags, "-shared") args := append(gogccflags, "-shared")
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin", "ios":
ext = "dylib" ext = "dylib"
args = append(args, "-undefined", "suppress", "-flat_namespace") args = append(args, "-undefined", "suppress", "-flat_namespace")
case "windows": case "windows":
@ -119,7 +119,7 @@ func TestSO(t *testing.T) {
cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
s := "LD_LIBRARY_PATH" s := "LD_LIBRARY_PATH"
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
s = "DYLD_LIBRARY_PATH" s = "DYLD_LIBRARY_PATH"
} }
cmd.Env = append(os.Environ(), s+"=.") cmd.Env = append(os.Environ(), s+"=.")

View file

@ -21,7 +21,7 @@ func requireTestSOSupported(t *testing.T) {
t.Helper() t.Helper()
switch runtime.GOARCH { switch runtime.GOARCH {
case "arm64": case "arm64":
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
t.Skip("No exec facility on iOS.") t.Skip("No exec facility on iOS.")
} }
case "ppc64": case "ppc64":
@ -74,7 +74,7 @@ func TestSO(t *testing.T) {
ext := "so" ext := "so"
args := append(gogccflags, "-shared") args := append(gogccflags, "-shared")
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin", "ios":
ext = "dylib" ext = "dylib"
args = append(args, "-undefined", "suppress", "-flat_namespace") args = append(args, "-undefined", "suppress", "-flat_namespace")
case "windows": case "windows":
@ -119,7 +119,7 @@ func TestSO(t *testing.T) {
cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
s := "LD_LIBRARY_PATH" s := "LD_LIBRARY_PATH"
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
s = "DYLD_LIBRARY_PATH" s = "DYLD_LIBRARY_PATH"
} }
cmd.Env = append(os.Environ(), s+"=.") cmd.Env = append(os.Environ(), s+"=.")

View file

@ -15,4 +15,4 @@ else
exit 1 exit 1
fi fi
exec $CLANG -arch $CLANGARCH -isysroot $SDK_PATH -mios-version-min=10.0 "$@" exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -mios-version-min=10.0 "$@"

View file

@ -11,6 +11,7 @@
// - Node.js // - Node.js
// - Electron // - Electron
// - Parcel // - Parcel
// - Webpack
if (typeof global !== "undefined") { if (typeof global !== "undefined") {
// global already exists // global already exists
@ -28,7 +29,7 @@
if (!global.fs && global.require) { if (!global.fs && global.require) {
const fs = require("fs"); const fs = require("fs");
if (Object.keys(fs) !== 0) { if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
global.fs = fs; global.fs = fs;
} }
} }
@ -556,6 +557,7 @@
} }
if ( if (
typeof module !== "undefined" &&
global.require && global.require &&
global.require.main === module && global.require.main === module &&
global.process && global.process &&

View file

@ -66,7 +66,7 @@ func statUnix(fi os.FileInfo, h *Header) error {
minor := uint32((dev & 0x00000000000000ff) >> 0) minor := uint32((dev & 0x00000000000000ff) >> 0)
minor |= uint32((dev & 0x00000ffffff00000) >> 12) minor |= uint32((dev & 0x00000ffffff00000) >> 12)
h.Devmajor, h.Devminor = int64(major), int64(minor) h.Devmajor, h.Devminor = int64(major), int64(minor)
case "darwin": case "darwin", "ios":
// Copied from golang.org/x/sys/unix/dev_darwin.go. // Copied from golang.org/x/sys/unix/dev_darwin.go.
major := uint32((dev >> 24) & 0xff) major := uint32((dev >> 24) & 0xff)
minor := uint32(dev & 0xffffff) minor := uint32(dev & 0xffffff)

View file

@ -425,7 +425,7 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
// of bytes in the combined first two elements, error). // of bytes in the combined first two elements, error).
// The complete result is equal to // The complete result is equal to
// `bytes.Join(append(fullBuffers, finalFragment), nil)`, which has a // `bytes.Join(append(fullBuffers, finalFragment), nil)`, which has a
// length of `totalLen`. The result is strucured in this way to allow callers // length of `totalLen`. The result is structured in this way to allow callers
// to minimize allocations and copies. // to minimize allocations and copies.
func (b *Reader) collectFragments(delim byte) (fullBuffers [][]byte, finalFragment []byte, totalLen int, err error) { func (b *Reader) collectFragments(delim byte) (fullBuffers [][]byte, finalFragment []byte, totalLen int, err error) {
var frag []byte var frag []byte

View file

@ -73,29 +73,37 @@ func testAddr2Line(t *testing.T, exepath, addr string) {
if err != nil { if err != nil {
t.Fatalf("Stat failed: %v", err) t.Fatalf("Stat failed: %v", err)
} }
// Debug paths are stored slash-separated, so convert to system-native. // Debug paths are stored slash-separated, so convert to system-native.
srcPath = filepath.FromSlash(srcPath) srcPath = filepath.FromSlash(srcPath)
fi2, err := os.Stat(srcPath) fi2, err := os.Stat(srcPath)
if gorootFinal := os.Getenv("GOROOT_FINAL"); gorootFinal != "" && strings.HasPrefix(srcPath, gorootFinal) {
if os.IsNotExist(err) || (err == nil && !os.SameFile(fi1, fi2)) { // If GOROOT_FINAL is set and srcPath is not the file we expect, perhaps
// srcPath has had GOROOT_FINAL substituted for GOROOT, and it doesn't // srcPath has had GOROOT_FINAL substituted for GOROOT and GOROOT hasn't been
// match the actual file. GOROOT probably hasn't been moved to its final // moved to its final location yet. If so, try the original location instead.
// location yet, so try the original location instead. if gorootFinal := os.Getenv("GOROOT_FINAL"); gorootFinal != "" &&
(os.IsNotExist(err) || (err == nil && !os.SameFile(fi1, fi2))) {
// srcPath is clean, but GOROOT_FINAL itself might not be.
// (See https://golang.org/issue/41447.)
gorootFinal = filepath.Clean(gorootFinal)
if strings.HasPrefix(srcPath, gorootFinal) {
fi2, err = os.Stat(runtime.GOROOT() + strings.TrimPrefix(srcPath, gorootFinal)) fi2, err = os.Stat(runtime.GOROOT() + strings.TrimPrefix(srcPath, gorootFinal))
} }
} }
if err != nil { if err != nil {
t.Fatalf("Stat failed: %v", err) t.Fatalf("Stat failed: %v", err)
} }
if !os.SameFile(fi1, fi2) { if !os.SameFile(fi1, fi2) {
t.Fatalf("addr2line_test.go and %s are not same file", srcPath) t.Fatalf("addr2line_test.go and %s are not same file", srcPath)
} }
if srcLineNo != "99" { if srcLineNo != "107" {
t.Fatalf("line number = %v; want 99", srcLineNo) t.Fatalf("line number = %v; want 107", srcLineNo)
} }
} }
// This is line 98. The test depends on that. // This is line 106. The test depends on that.
func TestAddr2Line(t *testing.T) { func TestAddr2Line(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)

View file

@ -87,7 +87,10 @@ var contexts = []*build.Context{
func contextName(c *build.Context) string { func contextName(c *build.Context) string {
s := c.GOOS + "-" + c.GOARCH s := c.GOOS + "-" + c.GOARCH
if c.CgoEnabled { if c.CgoEnabled {
return s + "-cgo" s += "-cgo"
}
if c.Dir != "" {
s += fmt.Sprintf(" [%s]", c.Dir)
} }
return s return s
} }
@ -478,6 +481,9 @@ func (w *Walker) loadImports() {
cmd := exec.Command(goCmd(), "list", "-e", "-deps", "-json", "std") cmd := exec.Command(goCmd(), "list", "-e", "-deps", "-json", "std")
cmd.Env = listEnv(w.context) cmd.Env = listEnv(w.context)
if w.context.Dir != "" {
cmd.Dir = w.context.Dir
}
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
log.Fatalf("loading imports: %v\n%s", err, out) log.Fatalf("loading imports: %v\n%s", err, out)
@ -491,6 +497,7 @@ func (w *Walker) loadImports() {
var pkg struct { var pkg struct {
ImportPath, Dir string ImportPath, Dir string
ImportMap map[string]string ImportMap map[string]string
Standard bool
} }
err := dec.Decode(&pkg) err := dec.Decode(&pkg)
if err == io.EOF { if err == io.EOF {
@ -503,11 +510,13 @@ func (w *Walker) loadImports() {
// - Package "unsafe" contains special signatures requiring // - Package "unsafe" contains special signatures requiring
// extra care when printing them - ignore since it is not // extra care when printing them - ignore since it is not
// going to change w/o a language change. // going to change w/o a language change.
// - internal and vendored packages do not contribute to our // - Internal and vendored packages do not contribute to our
// API surface. // API surface. (If we are running within the "std" module,
// vendored dependencies appear as themselves instead of
// their "vendor/" standard-library copies.)
// - 'go list std' does not include commands, which cannot be // - 'go list std' does not include commands, which cannot be
// imported anyway. // imported anyway.
if ip := pkg.ImportPath; ip != "unsafe" && !strings.HasPrefix(ip, "vendor/") && !internalPkg.MatchString(ip) { if ip := pkg.ImportPath; pkg.Standard && ip != "unsafe" && !strings.HasPrefix(ip, "vendor/") && !internalPkg.MatchString(ip) {
stdPackages = append(stdPackages, ip) stdPackages = append(stdPackages, ip)
} }
importDir[pkg.ImportPath] = pkg.Dir importDir[pkg.ImportPath] = pkg.Dir

View file

@ -216,3 +216,16 @@ func TestIssue29837(t *testing.T) {
} }
} }
} }
func TestIssue41358(t *testing.T) {
context := new(build.Context)
*context = build.Default
context.Dir = filepath.Join(context.GOROOT, "src")
w := NewWalker(context, context.Dir)
for _, pkg := range w.stdPackages {
if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
t.Fatalf("stdPackages contains unexpected package %s", pkg)
}
}
}

View file

@ -77,6 +77,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
SHA1H V5, V4 // a408285e SHA1H V5, V4 // a408285e
SHA1M V8.S4, V7, V6 // e620085e SHA1M V8.S4, V7, V6 // e620085e
SHA1P V11.S4, V10, V9 // 49110b5e SHA1P V11.S4, V10, V9 // 49110b5e
SHA512H V2.D2, V1, V0 // 208062ce
SHA512H2 V4.D2, V3, V2 // 628464ce
SHA512SU0 V9.D2, V8.D2 // 2881c0ce
SHA512SU1 V7.D2, V6.D2, V5.D2 // c58867ce
VADDV V0.S4, V0 // 00b8b14e VADDV V0.S4, V0 // 00b8b14e
VMOVI $82, V0.B16 // 40e6024f VMOVI $82, V0.B16 // 40e6024f
VUADDLV V6.B16, V6 // c638306e VUADDLV V6.B16, V6 // c638306e
@ -141,6 +145,37 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VZIP2 V10.D2, V13.D2, V3.D2 // a379ca4e VZIP2 V10.D2, V13.D2, V3.D2 // a379ca4e
VZIP1 V17.S2, V4.S2, V26.S2 // 9a38910e VZIP1 V17.S2, V4.S2, V26.S2 // 9a38910e
VZIP2 V25.S2, V14.S2, V25.S2 // d979990e VZIP2 V25.S2, V14.S2, V25.S2 // d979990e
VUXTL V30.B8, V30.H8 // dea7082f
VUXTL V30.H4, V29.S4 // dda7102f
VUXTL V29.S2, V2.D2 // a2a7202f
VUXTL2 V30.H8, V30.S4 // dea7106f
VUXTL2 V29.S4, V2.D2 // a2a7206f
VUXTL2 V30.B16, V2.H8 // c2a7086f
VBIT V21.B16, V25.B16, V4.B16 // 241fb56e
VBSL V23.B16, V3.B16, V7.B16 // 671c776e
VCMTST V2.B8, V29.B8, V2.B8 // a28f220e
VCMTST V2.D2, V23.D2, V3.D2 // e38ee24e
VSUB V2.B8, V30.B8, V30.B8 // de87222e
VUZP1 V0.B8, V30.B8, V1.B8 // c11b000e
VUZP1 V1.B16, V29.B16, V2.B16 // a21b014e
VUZP1 V2.H4, V28.H4, V3.H4 // 831b420e
VUZP1 V3.H8, V27.H8, V4.H8 // 641b434e
VUZP1 V28.S2, V2.S2, V5.S2 // 45189c0e
VUZP1 V29.S4, V1.S4, V6.S4 // 26189d4e
VUZP1 V30.D2, V0.D2, V7.D2 // 0718de4e
VUZP2 V0.D2, V30.D2, V1.D2 // c15bc04e
VUZP2 V30.D2, V0.D2, V29.D2 // 1d58de4e
VUSHLL $0, V30.B8, V30.H8 // dea7082f
VUSHLL $0, V30.H4, V29.S4 // dda7102f
VUSHLL $0, V29.S2, V2.D2 // a2a7202f
VUSHLL2 $0, V30.B16, V2.H8 // c2a7086f
VUSHLL2 $0, V30.H8, V30.S4 // dea7106f
VUSHLL2 $0, V29.S4, V2.D2 // a2a7206f
VUSHLL $7, V30.B8, V30.H8 // dea70f2f
VUSHLL $15, V30.H4, V29.S4 // dda71f2f
VUSHLL2 $31, V30.S4, V2.D2 // c2a73f6f
VBIF V0.B8, V30.B8, V1.B8 // c11fe02e
VBIF V30.B16, V0.B16, V2.B16 // 021cfe6e
MOVD (R2)(R6.SXTW), R4 // 44c866f8 MOVD (R2)(R6.SXTW), R4 // 44c866f8
MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8 MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8
MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8 MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8
@ -182,6 +217,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
FMOVS $(0.96875), F3 // 03f02d1e FMOVS $(0.96875), F3 // 03f02d1e
FMOVD $(28.0), F4 // 0490671e FMOVD $(28.0), F4 // 0490671e
// move a large constant to a Vd.
FMOVD $0x8040201008040201, V20 // FMOVD $-9205322385119247871, V20
FMOVQ $0x8040201008040202, V29 // FMOVQ $-9205322385119247870, V29
FMOVS (R2)(R6), F4 // FMOVS (R2)(R6*1), F4 // 446866bc FMOVS (R2)(R6), F4 // FMOVS (R2)(R6*1), F4 // 446866bc
FMOVS (R2)(R6<<2), F4 // 447866bc FMOVS (R2)(R6<<2), F4 // 447866bc
FMOVD (R2)(R6), F4 // FMOVD (R2)(R6*1), F4 // 446866fc FMOVD (R2)(R6), F4 // FMOVD (R2)(R6*1), F4 // 446866fc
@ -301,8 +340,19 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
MOVD $0x1111ffff1111aaaa, R1 // MOVD $1230045644216969898, R1 // a1aa8a922122a2f22122e2f2 MOVD $0x1111ffff1111aaaa, R1 // MOVD $1230045644216969898, R1 // a1aa8a922122a2f22122e2f2
MOVD $0, R1 // 010080d2 MOVD $0, R1 // 010080d2
MOVD $-1, R1 // 01008092 MOVD $-1, R1 // 01008092
MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2 MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2
MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92 MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92
MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // fb074091610b0091
MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // fb0740917f231c91
MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // fb08409161070091
MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // fbfc7f9161ff3f91
MOVD $-0x1(R7), R1 // MOVD $-1(R7), R1 // e10400d1
MOVD $-0x30(R7), R1 // MOVD $-48(R7), R1 // e1c000d1
MOVD $-0x708(R7), R1 // MOVD $-1800(R7), R1 // e1201cd1
MOVD $-0x2000(RSP), R1 // MOVD $-8192(RSP), R1 // e10b40d1
MOVD $-0x10000(RSP), RSP // MOVD $-65536(RSP), RSP // ff4340d1
// //
// CLS // CLS
@ -355,18 +405,22 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VLD4 (R15), [V10.H4, V11.H4, V12.H4, V13.H4] // ea05400c VLD4 (R15), [V10.H4, V11.H4, V12.H4, V13.H4] // ea05400c
VLD4.P 32(R24), [V31.B8, V0.B8, V1.B8, V2.B8] // 1f03df0c VLD4.P 32(R24), [V31.B8, V0.B8, V1.B8, V2.B8] // 1f03df0c
VLD4.P (R13)(R9), [V14.S2, V15.S2, V16.S2, V17.S2] // VLD4.P (R13)(R9*1), [V14.S2,V15.S2,V16.S2,V17.S2] // ae09c90c VLD4.P (R13)(R9), [V14.S2, V15.S2, V16.S2, V17.S2] // VLD4.P (R13)(R9*1), [V14.S2,V15.S2,V16.S2,V17.S2] // ae09c90c
VLD1R (R0), [V0.B16] // 00c0404d VLD1R (R1), [V9.B8] // 29c0400d
VLD1R.P 16(R0), [V0.B16] // 00c0df4d VLD1R.P (R1), [V9.B8] // 29c0df0d
VLD1R.P (R15)(R1), [V15.H4] // VLD1R.P (R15)(R1*1), [V15.H4] // efc5c10d VLD1R.P 1(R1), [V2.B8] // 22c0df0d
VLD2R (R15), [V15.H4, V16.H4] // efc5600d VLD1R.P 2(R1), [V2.H4] // 22c4df0d
VLD2R.P 32(R0), [V0.D2, V1.D2] // 00ccff4d VLD1R (R0), [V0.B16] // 00c0404d
VLD2R.P (R0)(R5), [V31.D1, V0.D1] // VLD2R.P (R0)(R5*1), [V31.D1, V0.D1] // 1fcce50d VLD1R.P (R0), [V0.B16] // 00c0df4d
VLD3R (RSP), [V31.S2, V0.S2, V1.S2] // ffeb400d VLD1R.P (R15)(R1), [V15.H4] // VLD1R.P (R15)(R1*1), [V15.H4] // efc5c10d
VLD3R.P 24(R15), [V15.H4, V16.H4, V17.H4] // efe5df0d VLD2R (R15), [V15.H4, V16.H4] // efc5600d
VLD3R.P (R15)(R6), [V15.H8, V16.H8, V17.H8] // VLD3R.P (R15)(R6*1), [V15.H8, V16.H8, V17.H8] // efe5c64d VLD2R.P 16(R0), [V0.D2, V1.D2] // 00ccff4d
VLD4R (R0), [V0.B8, V1.B8, V2.B8, V3.B8] // 00e0600d VLD2R.P (R0)(R5), [V31.D1, V0.D1] // VLD2R.P (R0)(R5*1), [V31.D1, V0.D1] // 1fcce50d
VLD4R.P 64(RSP), [V31.S4, V0.S4, V1.S4, V2.S4] // ffebff4d VLD3R (RSP), [V31.S2, V0.S2, V1.S2] // ffeb400d
VLD4R.P (R15)(R9), [V15.H4, V16.H4, V17.H4, V18.H4] // VLD4R.P (R15)(R9*1), [V15.H4, V16.H4, V17.H4, V18.H4] // efe5e90d VLD3R.P 6(R15), [V15.H4, V16.H4, V17.H4] // efe5df0d
VLD3R.P (R15)(R6), [V15.H8, V16.H8, V17.H8] // VLD3R.P (R15)(R6*1), [V15.H8, V16.H8, V17.H8] // efe5c64d
VLD4R (R0), [V0.B8, V1.B8, V2.B8, V3.B8] // 00e0600d
VLD4R.P 16(RSP), [V31.S4, V0.S4, V1.S4, V2.S4] // ffebff4d
VLD4R.P (R15)(R9), [V15.H4, V16.H4, V17.H4, V18.H4] // VLD4R.P (R15)(R9*1), [V15.H4, V16.H4, V17.H4, V18.H4] // efe5e90d
VST1.P [V24.S2], 8(R2) // 58789f0c VST1.P [V24.S2], 8(R2) // 58789f0c
VST1 [V29.S2, V30.S2], (R29) // bdab000c VST1 [V29.S2, V30.S2], (R29) // bdab000c
VST1 [V14.H4, V15.H4, V16.H4], (R27) // 6e67000c VST1 [V14.H4, V15.H4, V16.H4], (R27) // 6e67000c

View file

@ -591,7 +591,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8
FMOVS R8, F15 // 0f01271e FMOVS R8, F15 // 0f01271e
FMOVD F2, F9 // 4940601e FMOVD F2, F9 // 4940601e
FMOVS F4, F27 // 9b40201e FMOVS F4, F27 // 9b40201e
//TODO VFMOV $3.125, V8.2D // 28f5006f //TODO VFMOV $3.125, V8.D2 // 28f5006f
FMSUBS F13, F21, F13, F19 // b3d50d1f FMSUBS F13, F21, F13, F19 // b3d50d1f
FMSUBD F11, F7, F15, F31 // ff9d4b1f FMSUBD F11, F7, F15, F31 // ff9d4b1f
//TODO VFMUL V9.S[2], F21, F19 // b39a895f //TODO VFMUL V9.S[2], F21, F19 // b39a895f
@ -648,7 +648,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8
FSUBS F25, F23, F0 // e03a391e FSUBS F25, F23, F0 // e03a391e
FSUBD F11, F13, F24 // b8396b1e FSUBD F11, F13, F24 // b8396b1e
//TODO SCVTFSS F30, F20 // d4db215e //TODO SCVTFSS F30, F20 // d4db215e
//TODO VSCVTF V7.2S, V17.2S // f1d8210e //TODO VSCVTF V7.S2, V17.S2 // f1d8210e
SCVTFWS R3, F16 // 7000221e SCVTFWS R3, F16 // 7000221e
SCVTFWD R20, F4 // 8402621e SCVTFWD R20, F4 // 8402621e
SCVTFS R16, F12 // 0c02229e SCVTFS R16, F12 // 0c02229e

View file

@ -339,4 +339,18 @@ TEXT errors(SB),$0
MRS ICV_EOIR1_EL1, R3 // ERROR "system register is not readable" MRS ICV_EOIR1_EL1, R3 // ERROR "system register is not readable"
MRS PMSWINC_EL0, R3 // ERROR "system register is not readable" MRS PMSWINC_EL0, R3 // ERROR "system register is not readable"
MRS OSLAR_EL1, R3 // ERROR "system register is not readable" MRS OSLAR_EL1, R3 // ERROR "system register is not readable"
VLD3R.P 24(R15), [V15.H4,V16.H4,V17.H4] // ERROR "invalid post-increment offset"
VBIT V1.H4, V12.H4, V3.H4 // ERROR "invalid arrangement"
VBSL V1.D2, V12.D2, V3.D2 // ERROR "invalid arrangement"
VUXTL V30.D2, V30.H8 // ERROR "operand mismatch"
VUXTL2 V20.B8, V21.H8 // ERROR "operand mismatch"
VUXTL V3.D2, V4.B8 // ERROR "operand mismatch"
VUZP1 V0.B8, V30.B8, V1.B16 // ERROR "operand mismatch"
VUZP2 V0.Q1, V30.Q1, V1.Q1 // ERROR "invalid arrangement"
VUSHLL $0, V30.D2, V30.H8 // ERROR "operand mismatch"
VUSHLL2 $0, V20.B8, V21.H8 // ERROR "operand mismatch"
VUSHLL $8, V30.B8, V30.H8 // ERROR "shift amount out of range"
VUSHLL2 $32, V30.S4, V2.D2 // ERROR "shift amount out of range"
VBIF V0.B8, V1.B8, V2.B16 // ERROR "operand mismatch"
VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement"
RET RET

View file

@ -1037,6 +1037,7 @@ label1:
// VSX load with length X-form (also left-justified) // VSX load with length X-form (also left-justified)
LXVL R3,R4, VS0 LXVL R3,R4, VS0
LXVLL R3,R4, VS0 LXVLL R3,R4, VS0
LXVX R3,R4, VS0
// VSX load, DQ-form // VSX load, DQ-form
// <MNEMONIC> DQ(RA), XS produces // <MNEMONIC> DQ(RA), XS produces
// <mnemonic> XS, DQ(RA) // <mnemonic> XS, DQ(RA)
@ -1060,6 +1061,7 @@ label1:
// VSX store with length, X-form (also left-justified) // VSX store with length, X-form (also left-justified)
STXVL VS0, R3,R4 STXVL VS0, R3,R4
STXVLL VS0, R3,R4 STXVLL VS0, R3,R4
STXVX VS0, R3,R4
// VSX move from VSR, XX1-form // VSX move from VSR, XX1-form
// <MNEMONIC> XS,RA produces // <MNEMONIC> XS,RA produces

View file

@ -284,6 +284,10 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
RLDICLCC $0, R4, $15, R6 // 788603c1 RLDICLCC $0, R4, $15, R6 // 788603c1
RLDICR $0, R4, $15, R6 // 788603c4 RLDICR $0, R4, $15, R6 // 788603c4
RLDICRCC $0, R4, $15, R6 // 788603c5 RLDICRCC $0, R4, $15, R6 // 788603c5
RLDIC $0, R4, $15, R6 // 788603c8
RLDICCC $0, R4, $15, R6 // 788603c9
CLRLSLWI $16, R5, $8, R4 // 54a4861e
CLRLSLDI $2, R4, $24, R3 // 78831588
BEQ 0(PC) // 41820000 BEQ 0(PC) // 41820000
BGE 0(PC) // 40800000 BGE 0(PC) // 40800000
@ -595,11 +599,13 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
LXV 16(R3), VS1 // f4230011 LXV 16(R3), VS1 // f4230011
LXVL R3, R4, VS1 // 7c23221a LXVL R3, R4, VS1 // 7c23221a
LXVLL R3, R4, VS1 // 7c23225a LXVLL R3, R4, VS1 // 7c23225a
LXVX R3, R4, VS1 // 7c232218
LXSDX (R3)(R4), VS1 // 7c241c98 LXSDX (R3)(R4), VS1 // 7c241c98
STXVD2X VS1, (R3)(R4) // 7c241f98 STXVD2X VS1, (R3)(R4) // 7c241f98
STXV VS1,16(R3) // f4230015 STXV VS1,16(R3) // f4230015
STXVL VS1, R3, R4 // 7c23231a STXVL VS1, R3, R4 // 7c23231a
STXVLL VS1, R3, R4 // 7c23235a STXVLL VS1, R3, R4 // 7c23235a
STXVX VS1, R3, R4 // 7c232318
STXSDX VS1, (R3)(R4) // 7c241d98 STXSDX VS1, (R3)(R4) // 7c241d98
LXSIWAX (R3)(R4), VS1 // 7c241898 LXSIWAX (R3)(R4), VS1 // 7c241898
STXSIWX VS1, (R3)(R4) // 7c241918 STXSIWX VS1, (R3)(R4) // 7c241918

View file

@ -17,7 +17,6 @@ import (
var ( var (
Debug = flag.Bool("debug", false, "dump instructions as they are parsed") Debug = flag.Bool("debug", false, "dump instructions as they are parsed")
OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument") OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument")
PrintOut = flag.Bool("S", false, "print assembly and machine code")
TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths") TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths")
Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library") Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library")
Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries") Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries")
@ -25,19 +24,19 @@ var (
SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble") SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
Importpath = flag.String("p", "", "set expected package import to path") Importpath = flag.String("p", "", "set expected package import to path")
Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)")
Go115Newobj = flag.Bool("go115newobj", true, "use new object file format")
) )
var ( var (
D MultiFlag D MultiFlag
I MultiFlag I MultiFlag
PrintOut int
) )
func init() { func init() {
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times") flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
flag.Var(&I, "I", "include directory; can be set multiple times") flag.Var(&I, "I", "include directory; can be set multiple times")
objabi.AddVersionFlag() // -V objabi.AddVersionFlag() // -V
objabi.Flagcount("S", "print assembly and machine code", &PrintOut)
} }
// MultiFlag allows setting a value multiple times to collect a list, as in -I=dir1 -I=dir2. // MultiFlag allows setting a value multiple times to collect a list, as in -I=dir1 -I=dir2.

View file

@ -35,13 +35,11 @@ func main() {
flags.Parse() flags.Parse()
ctxt := obj.Linknew(architecture.LinkArch) ctxt := obj.Linknew(architecture.LinkArch)
if *flags.PrintOut { ctxt.Debugasm = flags.PrintOut
ctxt.Debugasm = 1
}
ctxt.Flag_dynlink = *flags.Dynlink ctxt.Flag_dynlink = *flags.Dynlink
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
ctxt.Flag_go115newobj = *flags.Go115Newobj
ctxt.IsAsm = true ctxt.IsAsm = true
ctxt.Pkgpath = *flags.Importpath
switch *flags.Spectre { switch *flags.Spectre {
default: default:
log.Printf("unknown setting -spectre=%s", *flags.Spectre) log.Printf("unknown setting -spectre=%s", *flags.Spectre)
@ -97,8 +95,8 @@ func main() {
} }
} }
if ok && !*flags.SymABIs { if ok && !*flags.SymABIs {
ctxt.NumberSyms(true) ctxt.NumberSyms()
obj.WriteObjFile(ctxt, buf, "") obj.WriteObjFile(ctxt, buf)
} }
if !ok || diag { if !ok || diag {
if failedFile != "" { if failedFile != "" {

View file

@ -112,6 +112,13 @@ The default C and C++ compilers may be changed by the CC and CXX
environment variables, respectively; those environment variables environment variables, respectively; those environment variables
may include command line options. may include command line options.
The cgo tool will always invoke the C compiler with the source file's
directory in the include path; i.e. -I${SRCDIR} is always implied. This
means that if a header file foo/bar.h exists both in the source
directory and also in the system include directory (or some other place
specified by a -I flag), then "#include <foo/bar.h>" will always find the
local version in preference to any other version.
The cgo tool is enabled by default for native builds on systems where The cgo tool is enabled by default for native builds on systems where
it is expected to work. It is disabled by default when it is expected to work. It is disabled by default when
cross-compiling. You can control this by setting the CGO_ENABLED cross-compiling. You can control this by setting the CGO_ENABLED

View file

@ -298,7 +298,7 @@ func (p *Package) guessKinds(f *File) []*Name {
continue continue
} }
if goos == "darwin" && strings.HasSuffix(n.C, "Ref") { if (goos == "darwin" || goos == "ios") && strings.HasSuffix(n.C, "Ref") {
// For FooRef, find out if FooGetTypeID exists. // For FooRef, find out if FooGetTypeID exists.
s := n.C[:len(n.C)-3] + "GetTypeID" s := n.C[:len(n.C)-3] + "GetTypeID"
n := &Name{Go: s, C: s} n := &Name{Go: s, C: s}
@ -369,7 +369,18 @@ func (p *Package) guessKinds(f *File) []*Name {
fmt.Fprintf(&b, "#line 1 \"completed\"\n"+ fmt.Fprintf(&b, "#line 1 \"completed\"\n"+
"int __cgo__1 = __cgo__2;\n") "int __cgo__1 = __cgo__2;\n")
stderr := p.gccErrors(b.Bytes()) // We need to parse the output from this gcc command, so ensure that it
// doesn't have any ANSI escape sequences in it. (TERM=dumb is
// insufficient; if the user specifies CGO_CFLAGS=-fdiagnostics-color,
// GCC will ignore TERM, and GCC can also be configured at compile-time
// to ignore TERM.)
stderr := p.gccErrors(b.Bytes(), "-fdiagnostics-color=never")
if strings.Contains(stderr, "unrecognized command line option") {
// We're using an old version of GCC that doesn't understand
// -fdiagnostics-color. Those versions can't print color anyway,
// so just rerun without that option.
stderr = p.gccErrors(b.Bytes())
}
if stderr == "" { if stderr == "" {
fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
} }
@ -1970,22 +1981,25 @@ func (p *Package) gccDefines(stdin []byte) string {
// gccErrors runs gcc over the C program stdin and returns // gccErrors runs gcc over the C program stdin and returns
// the errors that gcc prints. That is, this function expects // the errors that gcc prints. That is, this function expects
// gcc to fail. // gcc to fail.
func (p *Package) gccErrors(stdin []byte) string { func (p *Package) gccErrors(stdin []byte, extraArgs ...string) string {
// TODO(rsc): require failure // TODO(rsc): require failure
args := p.gccCmd() args := p.gccCmd()
// Optimization options can confuse the error messages; remove them. // Optimization options can confuse the error messages; remove them.
nargs := make([]string, 0, len(args)) nargs := make([]string, 0, len(args)+len(extraArgs))
for _, arg := range args { for _, arg := range args {
if !strings.HasPrefix(arg, "-O") { if !strings.HasPrefix(arg, "-O") {
nargs = append(nargs, arg) nargs = append(nargs, arg)
} }
} }
// Force -O0 optimization but keep the trailing "-" at the end. // Force -O0 optimization and append extra arguments, but keep the
nargs = append(nargs, "-O0") // trailing "-" at the end.
nl := len(nargs) li := len(nargs) - 1
nargs[nl-2], nargs[nl-1] = nargs[nl-1], nargs[nl-2] last := nargs[li]
nargs[li] = "-O0"
nargs = append(nargs, extraArgs...)
nargs = append(nargs, last)
if *debugGcc { if *debugGcc {
fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(nargs, " ")) fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(nargs, " "))
@ -2434,6 +2448,18 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt := *t tt := *t
tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}} tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}}
tt.Go = c.Ident("struct{}") tt.Go = c.Ident("struct{}")
if dt.Kind == "struct" {
// We don't know what the representation of this struct is, so don't let
// anyone allocate one on the Go side. As a side effect of this annotation,
// pointers to this type will not be considered pointers in Go. They won't
// get writebarrier-ed or adjusted during a stack copy. This should handle
// all the cases badPointerTypedef used to handle, but hopefully will
// continue to work going forward without any more need for cgo changes.
tt.NotInHeap = true
// TODO: we should probably do the same for unions. Unions can't live
// on the Go heap, right? It currently doesn't work for unions because
// they are defined as a type alias for struct{}, not a defined type.
}
typedef[name.Name] = &tt typedef[name.Name] = &tt
break break
} }
@ -2504,6 +2530,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
} }
t.Go = name t.Go = name
t.BadPointer = sub.BadPointer t.BadPointer = sub.BadPointer
t.NotInHeap = sub.NotInHeap
if unionWithPointer[sub.Go] { if unionWithPointer[sub.Go] {
unionWithPointer[t.Go] = true unionWithPointer[t.Go] = true
} }
@ -2514,6 +2541,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt := *t tt := *t
tt.Go = sub.Go tt.Go = sub.Go
tt.BadPointer = sub.BadPointer tt.BadPointer = sub.BadPointer
tt.NotInHeap = sub.NotInHeap
typedef[name.Name] = &tt typedef[name.Name] = &tt
} }
@ -2817,21 +2845,11 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
tgo := t.Go tgo := t.Go
size := t.Size size := t.Size
talign := t.Align talign := t.Align
if f.BitSize > 0 { if f.BitOffset > 0 || f.BitSize > 0 {
switch f.BitSize { // The layout of bitfields is implementation defined,
case 8, 16, 32, 64: // so we don't know how they correspond to Go fields
default: // even if they are aligned at byte boundaries.
continue continue
}
size = f.BitSize / 8
name := tgo.(*ast.Ident).String()
if strings.HasPrefix(name, "int") {
name = "int"
} else {
name = "uint"
}
tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
talign = size
} }
if talign > 0 && f.ByteOffset%talign != 0 { if talign > 0 && f.ByteOffset%talign != 0 {
@ -3022,6 +3040,7 @@ func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
// non-pointers in this type. // non-pointers in this type.
// TODO: Currently our best solution is to find these manually and list them as // TODO: Currently our best solution is to find these manually and list them as
// they come up. A better solution is desired. // they come up. A better solution is desired.
// Note: DEPRECATED. There is now a better solution. Search for NotInHeap in this file.
func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
if c.badCFType(dt) { if c.badCFType(dt) {
return true return true
@ -3056,7 +3075,7 @@ func (c *typeConv) badCFType(dt *dwarf.TypedefType) bool {
// We identify the correct set of types as those ending in Ref and for which // We identify the correct set of types as those ending in Ref and for which
// there exists a corresponding GetTypeID function. // there exists a corresponding GetTypeID function.
// See comment below for details about the bad pointers. // See comment below for details about the bad pointers.
if goos != "darwin" { if goos != "darwin" && goos != "ios" {
return false return false
} }
s := dt.Name s := dt.Name

View file

@ -151,7 +151,8 @@ type Type struct {
Go ast.Expr Go ast.Expr
EnumValues map[string]int64 EnumValues map[string]int64
Typedef string Typedef string
BadPointer bool BadPointer bool // this pointer type should be represented as a uintptr (deprecated)
NotInHeap bool // this type should have a go:notinheap annotation
} }
// A FuncType collects information about a function type in both the C and Go worlds. // A FuncType collects information about a function type in both the C and Go worlds.

View file

@ -108,6 +108,9 @@ func (p *Package) writeDefs() {
sort.Strings(typedefNames) sort.Strings(typedefNames)
for _, name := range typedefNames { for _, name := range typedefNames {
def := typedef[name] def := typedef[name]
if def.NotInHeap {
fmt.Fprintf(fgo2, "//go:notinheap\n")
}
fmt.Fprintf(fgo2, "type %s ", name) fmt.Fprintf(fgo2, "type %s ", name)
// We don't have source info for these types, so write them out without source info. // We don't have source info for these types, so write them out without source info.
// Otherwise types would look like: // Otherwise types would look like:
@ -123,7 +126,9 @@ func (p *Package) writeDefs() {
// Moreover, empty file name makes compile emit no source debug info at all. // Moreover, empty file name makes compile emit no source debug info at all.
var buf bytes.Buffer var buf bytes.Buffer
noSourceConf.Fprint(&buf, fset, def.Go) noSourceConf.Fprint(&buf, fset, def.Go)
if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) { if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) ||
strings.HasPrefix(name, "_Ctype_enum_") ||
strings.HasPrefix(name, "_Ctype_union_") {
// This typedef is of the form `typedef a b` and should be an alias. // This typedef is of the form `typedef a b` and should be an alias.
fmt.Fprintf(fgo2, "= ") fmt.Fprintf(fgo2, "= ")
} }
@ -241,6 +246,7 @@ func (p *Package) writeDefs() {
if err != nil { if err != nil {
fatalf("%s", err) fatalf("%s", err)
} }
defer fgcch.Close()
_, err = io.Copy(fexp, fgcch) _, err = io.Copy(fexp, fgcch)
if err != nil { if err != nil {
fatalf("%s", err) fatalf("%s", err)

View file

@ -319,8 +319,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// TODO(khr): issue only the -1 fixup code we need. // TODO(khr): issue only the -1 fixup code we need.
// For instance, if only the quotient is used, no point in zeroing the remainder. // For instance, if only the quotient is used, no point in zeroing the remainder.
j1.To.Val = n1 j1.To.SetTarget(n1)
j2.To.Val = s.Pc() j2.To.SetTarget(s.Pc())
} }
case ssa.OpAMD64HMULQ, ssa.OpAMD64HMULL, ssa.OpAMD64HMULQU, ssa.OpAMD64HMULLU: case ssa.OpAMD64HMULQ, ssa.OpAMD64HMULL, ssa.OpAMD64HMULQU, ssa.OpAMD64HMULLU:
@ -874,7 +874,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
ssa.OpAMD64SUBLloadidx1, ssa.OpAMD64SUBLloadidx4, ssa.OpAMD64SUBLloadidx8, ssa.OpAMD64SUBQloadidx1, ssa.OpAMD64SUBQloadidx8, ssa.OpAMD64SUBLloadidx1, ssa.OpAMD64SUBLloadidx4, ssa.OpAMD64SUBLloadidx8, ssa.OpAMD64SUBQloadidx1, ssa.OpAMD64SUBQloadidx8,
ssa.OpAMD64ANDLloadidx1, ssa.OpAMD64ANDLloadidx4, ssa.OpAMD64ANDLloadidx8, ssa.OpAMD64ANDQloadidx1, ssa.OpAMD64ANDQloadidx8, ssa.OpAMD64ANDLloadidx1, ssa.OpAMD64ANDLloadidx4, ssa.OpAMD64ANDLloadidx8, ssa.OpAMD64ANDQloadidx1, ssa.OpAMD64ANDQloadidx8,
ssa.OpAMD64ORLloadidx1, ssa.OpAMD64ORLloadidx4, ssa.OpAMD64ORLloadidx8, ssa.OpAMD64ORQloadidx1, ssa.OpAMD64ORQloadidx8, ssa.OpAMD64ORLloadidx1, ssa.OpAMD64ORLloadidx4, ssa.OpAMD64ORLloadidx8, ssa.OpAMD64ORQloadidx1, ssa.OpAMD64ORQloadidx8,
ssa.OpAMD64XORLloadidx1, ssa.OpAMD64XORLloadidx4, ssa.OpAMD64XORLloadidx8, ssa.OpAMD64XORQloadidx1, ssa.OpAMD64XORQloadidx8: ssa.OpAMD64XORLloadidx1, ssa.OpAMD64XORLloadidx4, ssa.OpAMD64XORLloadidx8, ssa.OpAMD64XORQloadidx1, ssa.OpAMD64XORQloadidx8,
ssa.OpAMD64ADDSSloadidx1, ssa.OpAMD64ADDSSloadidx4, ssa.OpAMD64ADDSDloadidx1, ssa.OpAMD64ADDSDloadidx8,
ssa.OpAMD64SUBSSloadidx1, ssa.OpAMD64SUBSSloadidx4, ssa.OpAMD64SUBSDloadidx1, ssa.OpAMD64SUBSDloadidx8,
ssa.OpAMD64MULSSloadidx1, ssa.OpAMD64MULSSloadidx4, ssa.OpAMD64MULSDloadidx1, ssa.OpAMD64MULSDloadidx8,
ssa.OpAMD64DIVSSloadidx1, ssa.OpAMD64DIVSSloadidx4, ssa.OpAMD64DIVSDloadidx1, ssa.OpAMD64DIVSDloadidx8:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
r, i := v.Args[1].Reg(), v.Args[2].Reg() r, i := v.Args[1].Reg(), v.Args[2].Reg()

View file

@ -11,7 +11,7 @@ import (
"cmd/internal/objabi" "cmd/internal/objabi"
) )
var darwin = objabi.GOOS == "darwin" var darwin = objabi.GOOS == "darwin" || objabi.GOOS == "ios"
func padframe(frame int64) int64 { func padframe(frame int64) int64 {
// arm64 requires that the frame size (not counting saved FP&LR) // arm64 requires that the frame size (not counting saved FP&LR)

View file

@ -816,7 +816,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
} }
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
p.From.Reg = condBits[v.Aux.(ssa.Op)] p.From.Reg = condBits[ssa.Op(v.AuxInt)]
p.Reg = v.Args[0].Reg() p.Reg = v.Args[0].Reg()
p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r1}) p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r1})
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG

View file

@ -392,7 +392,7 @@ func genhash(t *types.Type) *obj.LSym {
} }
fn.Func.SetNilCheckDisabled(true) fn.Func.SetNilCheckDisabled(true)
funccompile(fn) xtop = append(xtop, fn)
// Build closure. It doesn't close over any variables, so // Build closure. It doesn't close over any variables, so
// it contains just the function pointer. // it contains just the function pointer.
@ -429,8 +429,7 @@ func hashfor(t *types.Type) *Node {
} }
n := newname(sym) n := newname(sym)
n.SetClass(PFUNC) setNodeNameFunc(n)
n.Sym.SetFunc(true)
n.Type = functype(nil, []*Node{ n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)), anonfield(types.NewPtr(t)),
anonfield(types.Types[TUINTPTR]), anonfield(types.Types[TUINTPTR]),
@ -646,17 +645,11 @@ func geneq(t *types.Type) *obj.LSym {
// Build a list of conditions to satisfy. // Build a list of conditions to satisfy.
// The conditions are a list-of-lists. Conditions are reorderable // The conditions are a list-of-lists. Conditions are reorderable
// within each inner list. The outer lists must be evaluated in order. // within each inner list. The outer lists must be evaluated in order.
// Even within each inner list, track their order so that we can preserve var conds [][]*Node
// aspects of that order. (TODO: latter part needed?) conds = append(conds, []*Node{})
type nodeIdx struct {
n *Node
idx int
}
var conds [][]nodeIdx
conds = append(conds, []nodeIdx{})
and := func(n *Node) { and := func(n *Node) {
i := len(conds) - 1 i := len(conds) - 1
conds[i] = append(conds[i], nodeIdx{n: n, idx: len(conds[i])}) conds[i] = append(conds[i], n)
} }
// Walk the struct using memequal for runs of AMEM // Walk the struct using memequal for runs of AMEM
@ -674,7 +667,7 @@ func geneq(t *types.Type) *obj.LSym {
if !IsRegularMemory(f.Type) { if !IsRegularMemory(f.Type) {
if EqCanPanic(f.Type) { if EqCanPanic(f.Type) {
// Enforce ordering by starting a new set of reorderable conditions. // Enforce ordering by starting a new set of reorderable conditions.
conds = append(conds, []nodeIdx{}) conds = append(conds, []*Node{})
} }
p := nodSym(OXDOT, np, f.Sym) p := nodSym(OXDOT, np, f.Sym)
q := nodSym(OXDOT, nq, f.Sym) q := nodSym(OXDOT, nq, f.Sym)
@ -688,7 +681,7 @@ func geneq(t *types.Type) *obj.LSym {
} }
if EqCanPanic(f.Type) { if EqCanPanic(f.Type) {
// Also enforce ordering after something that can panic. // Also enforce ordering after something that can panic.
conds = append(conds, []nodeIdx{}) conds = append(conds, []*Node{})
} }
i++ i++
continue continue
@ -713,14 +706,13 @@ func geneq(t *types.Type) *obj.LSym {
// Sort conditions to put runtime calls last. // Sort conditions to put runtime calls last.
// Preserve the rest of the ordering. // Preserve the rest of the ordering.
var flatConds []nodeIdx var flatConds []*Node
for _, c := range conds { for _, c := range conds {
isCall := func(n *Node) bool {
return n.Op == OCALL || n.Op == OCALLFUNC
}
sort.SliceStable(c, func(i, j int) bool { sort.SliceStable(c, func(i, j int) bool {
x, y := c[i], c[j] return !isCall(c[i]) && isCall(c[j])
if (x.n.Op != OCALL) == (y.n.Op != OCALL) {
return x.idx < y.idx
}
return x.n.Op != OCALL
}) })
flatConds = append(flatConds, c...) flatConds = append(flatConds, c...)
} }
@ -729,9 +721,9 @@ func geneq(t *types.Type) *obj.LSym {
if len(flatConds) == 0 { if len(flatConds) == 0 {
cond = nodbool(true) cond = nodbool(true)
} else { } else {
cond = flatConds[0].n cond = flatConds[0]
for _, c := range flatConds[1:] { for _, c := range flatConds[1:] {
cond = nod(OANDAND, cond, c.n) cond = nod(OANDAND, cond, c)
} }
} }
@ -762,7 +754,7 @@ func geneq(t *types.Type) *obj.LSym {
// neither of which can be nil, and our comparisons // neither of which can be nil, and our comparisons
// are shallow. // are shallow.
fn.Func.SetNilCheckDisabled(true) fn.Func.SetNilCheckDisabled(true)
funccompile(fn) xtop = append(xtop, fn)
// Generate a closure which points at the function we just generated. // Generate a closure which points at the function we just generated.
dsymptr(closure, 0, sym.Linksym(), 0) dsymptr(closure, 0, sym.Linksym(), 0)

View file

@ -64,69 +64,68 @@ var runtimeDecls = [...]struct {
{"stringtoslicebyte", funcTag, 49}, {"stringtoslicebyte", funcTag, 49},
{"stringtoslicerune", funcTag, 52}, {"stringtoslicerune", funcTag, 52},
{"slicecopy", funcTag, 53}, {"slicecopy", funcTag, 53},
{"slicestringcopy", funcTag, 54}, {"decoderune", funcTag, 54},
{"decoderune", funcTag, 55}, {"countrunes", funcTag, 55},
{"countrunes", funcTag, 56}, {"convI2I", funcTag, 56},
{"convI2I", funcTag, 57}, {"convT16", funcTag, 57},
{"convT16", funcTag, 58}, {"convT32", funcTag, 57},
{"convT32", funcTag, 58}, {"convT64", funcTag, 57},
{"convT64", funcTag, 58}, {"convTstring", funcTag, 57},
{"convTstring", funcTag, 58}, {"convTslice", funcTag, 57},
{"convTslice", funcTag, 58}, {"convT2E", funcTag, 58},
{"convT2E", funcTag, 59}, {"convT2Enoptr", funcTag, 58},
{"convT2Enoptr", funcTag, 59}, {"convT2I", funcTag, 58},
{"convT2I", funcTag, 59}, {"convT2Inoptr", funcTag, 58},
{"convT2Inoptr", funcTag, 59}, {"assertE2I", funcTag, 56},
{"assertE2I", funcTag, 57}, {"assertE2I2", funcTag, 59},
{"assertE2I2", funcTag, 60}, {"assertI2I", funcTag, 56},
{"assertI2I", funcTag, 57}, {"assertI2I2", funcTag, 59},
{"assertI2I2", funcTag, 60}, {"panicdottypeE", funcTag, 60},
{"panicdottypeE", funcTag, 61}, {"panicdottypeI", funcTag, 60},
{"panicdottypeI", funcTag, 61}, {"panicnildottype", funcTag, 61},
{"panicnildottype", funcTag, 62}, {"ifaceeq", funcTag, 63},
{"ifaceeq", funcTag, 64}, {"efaceeq", funcTag, 63},
{"efaceeq", funcTag, 64}, {"fastrand", funcTag, 65},
{"fastrand", funcTag, 66}, {"makemap64", funcTag, 67},
{"makemap64", funcTag, 68}, {"makemap", funcTag, 68},
{"makemap", funcTag, 69}, {"makemap_small", funcTag, 69},
{"makemap_small", funcTag, 70}, {"mapaccess1", funcTag, 70},
{"mapaccess1", funcTag, 71}, {"mapaccess1_fast32", funcTag, 71},
{"mapaccess1_fast32", funcTag, 72}, {"mapaccess1_fast64", funcTag, 71},
{"mapaccess1_fast64", funcTag, 72}, {"mapaccess1_faststr", funcTag, 71},
{"mapaccess1_faststr", funcTag, 72}, {"mapaccess1_fat", funcTag, 72},
{"mapaccess1_fat", funcTag, 73}, {"mapaccess2", funcTag, 73},
{"mapaccess2", funcTag, 74}, {"mapaccess2_fast32", funcTag, 74},
{"mapaccess2_fast32", funcTag, 75}, {"mapaccess2_fast64", funcTag, 74},
{"mapaccess2_fast64", funcTag, 75}, {"mapaccess2_faststr", funcTag, 74},
{"mapaccess2_faststr", funcTag, 75}, {"mapaccess2_fat", funcTag, 75},
{"mapaccess2_fat", funcTag, 76}, {"mapassign", funcTag, 70},
{"mapassign", funcTag, 71}, {"mapassign_fast32", funcTag, 71},
{"mapassign_fast32", funcTag, 72}, {"mapassign_fast32ptr", funcTag, 71},
{"mapassign_fast32ptr", funcTag, 72}, {"mapassign_fast64", funcTag, 71},
{"mapassign_fast64", funcTag, 72}, {"mapassign_fast64ptr", funcTag, 71},
{"mapassign_fast64ptr", funcTag, 72}, {"mapassign_faststr", funcTag, 71},
{"mapassign_faststr", funcTag, 72}, {"mapiterinit", funcTag, 76},
{"mapiterinit", funcTag, 77}, {"mapdelete", funcTag, 76},
{"mapdelete", funcTag, 77}, {"mapdelete_fast32", funcTag, 77},
{"mapdelete_fast32", funcTag, 78}, {"mapdelete_fast64", funcTag, 77},
{"mapdelete_fast64", funcTag, 78}, {"mapdelete_faststr", funcTag, 77},
{"mapdelete_faststr", funcTag, 78}, {"mapiternext", funcTag, 78},
{"mapiternext", funcTag, 79}, {"mapclear", funcTag, 79},
{"mapclear", funcTag, 80}, {"makechan64", funcTag, 81},
{"makechan64", funcTag, 82}, {"makechan", funcTag, 82},
{"makechan", funcTag, 83}, {"chanrecv1", funcTag, 84},
{"chanrecv1", funcTag, 85}, {"chanrecv2", funcTag, 85},
{"chanrecv2", funcTag, 86}, {"chansend1", funcTag, 87},
{"chansend1", funcTag, 88},
{"closechan", funcTag, 30}, {"closechan", funcTag, 30},
{"writeBarrier", varTag, 90}, {"writeBarrier", varTag, 89},
{"typedmemmove", funcTag, 91}, {"typedmemmove", funcTag, 90},
{"typedmemclr", funcTag, 92}, {"typedmemclr", funcTag, 91},
{"typedslicecopy", funcTag, 93}, {"typedslicecopy", funcTag, 92},
{"selectnbsend", funcTag, 94}, {"selectnbsend", funcTag, 93},
{"selectnbrecv", funcTag, 95}, {"selectnbrecv", funcTag, 94},
{"selectnbrecv2", funcTag, 97}, {"selectnbrecv2", funcTag, 96},
{"selectsetpc", funcTag, 62}, {"selectsetpc", funcTag, 97},
{"selectgo", funcTag, 98}, {"selectgo", funcTag, 98},
{"block", funcTag, 9}, {"block", funcTag, 9},
{"makeslice", funcTag, 99}, {"makeslice", funcTag, 99},
@ -257,51 +256,51 @@ func runtimeTypes() []*types.Type {
typs[51] = types.NewPtr(typs[50]) typs[51] = types.NewPtr(typs[50])
typs[52] = functype(nil, []*Node{anonfield(typs[51]), anonfield(typs[28])}, []*Node{anonfield(typs[46])}) typs[52] = functype(nil, []*Node{anonfield(typs[51]), anonfield(typs[28])}, []*Node{anonfield(typs[46])})
typs[53] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])}) typs[53] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])})
typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[28])}, []*Node{anonfield(typs[15])}) typs[54] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])})
typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[45]), anonfield(typs[15])}) typs[55] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])})
typs[56] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])}) typs[56] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])})
typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])}) typs[57] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])})
typs[58] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])}) typs[58] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])})
typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])}) typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])})
typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])}) typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil)
typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil) typs[61] = functype(nil, []*Node{anonfield(typs[1])}, nil)
typs[62] = functype(nil, []*Node{anonfield(typs[1])}, nil) typs[62] = types.NewPtr(typs[5])
typs[63] = types.NewPtr(typs[5]) typs[63] = functype(nil, []*Node{anonfield(typs[62]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])})
typs[64] = functype(nil, []*Node{anonfield(typs[63]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])}) typs[64] = types.Types[TUINT32]
typs[65] = types.Types[TUINT32] typs[65] = functype(nil, nil, []*Node{anonfield(typs[64])})
typs[66] = functype(nil, nil, []*Node{anonfield(typs[65])}) typs[66] = types.NewMap(typs[2], typs[2])
typs[67] = types.NewMap(typs[2], typs[2]) typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[66])})
typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[66])})
typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[67])}) typs[69] = functype(nil, nil, []*Node{anonfield(typs[66])})
typs[70] = functype(nil, nil, []*Node{anonfield(typs[67])}) typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])})
typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])})
typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])})
typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])}) typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[3])}, nil)
typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, nil) typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66]), anonfield(typs[2])}, nil)
typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, nil) typs[78] = functype(nil, []*Node{anonfield(typs[3])}, nil)
typs[79] = functype(nil, []*Node{anonfield(typs[3])}, nil) typs[79] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[66])}, nil)
typs[80] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67])}, nil) typs[80] = types.NewChan(typs[2], types.Cboth)
typs[81] = types.NewChan(typs[2], types.Cboth) typs[81] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[80])})
typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[81])}) typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[80])})
typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[81])}) typs[83] = types.NewChan(typs[2], types.Crecv)
typs[84] = types.NewChan(typs[2], types.Crecv) typs[84] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, nil)
typs[85] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, nil) typs[85] = functype(nil, []*Node{anonfield(typs[83]), anonfield(typs[3])}, []*Node{anonfield(typs[6])})
typs[86] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) typs[86] = types.NewChan(typs[2], types.Csend)
typs[87] = types.NewChan(typs[2], types.Csend) typs[87] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, nil)
typs[88] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, nil) typs[88] = types.NewArray(typs[0], 3)
typs[89] = types.NewArray(typs[0], 3) typs[89] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[88]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])})
typs[90] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[89]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])}) typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) typs[93] = functype(nil, []*Node{anonfield(typs[86]), anonfield(typs[3])}, []*Node{anonfield(typs[6])})
typs[94] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, []*Node{anonfield(typs[6])}) typs[94] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[83])}, []*Node{anonfield(typs[6])})
typs[95] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) typs[95] = types.NewPtr(typs[6])
typs[96] = types.NewPtr(typs[6]) typs[96] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[95]), anonfield(typs[83])}, []*Node{anonfield(typs[6])})
typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[96]), anonfield(typs[84])}, []*Node{anonfield(typs[6])}) typs[97] = functype(nil, []*Node{anonfield(typs[62])}, nil)
typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[15]), anonfield(typs[6])}) typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])})
typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])}) typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])})
typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])}) typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])})
typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])}) typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])})
@ -318,10 +317,10 @@ func runtimeTypes() []*types.Type {
typs[112] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])}) typs[112] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])})
typs[113] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])}) typs[113] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])})
typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])}) typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])})
typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[65])}) typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[64])})
typs[116] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])}) typs[116] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])})
typs[117] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])}) typs[117] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])})
typs[118] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])}) typs[118] = functype(nil, []*Node{anonfield(typs[64])}, []*Node{anonfield(typs[20])})
typs[119] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])}) typs[119] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])})
typs[120] = functype(nil, []*Node{anonfield(typs[5])}, nil) typs[120] = functype(nil, []*Node{anonfield(typs[5])}, nil)
typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil) typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil)
@ -332,7 +331,7 @@ func runtimeTypes() []*types.Type {
typs[126] = functype(nil, []*Node{anonfield(typs[125]), anonfield(typs[125])}, nil) typs[126] = functype(nil, []*Node{anonfield(typs[125]), anonfield(typs[125])}, nil)
typs[127] = types.Types[TUINT16] typs[127] = types.Types[TUINT16]
typs[128] = functype(nil, []*Node{anonfield(typs[127]), anonfield(typs[127])}, nil) typs[128] = functype(nil, []*Node{anonfield(typs[127]), anonfield(typs[127])}, nil)
typs[129] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil) typs[129] = functype(nil, []*Node{anonfield(typs[64]), anonfield(typs[64])}, nil)
typs[130] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil) typs[130] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil)
return typs[:] return typs[:]
} }

View file

@ -75,8 +75,7 @@ func slicebytetostringtmp(ptr *byte, n int) string
func slicerunetostring(*[32]byte, []rune) string func slicerunetostring(*[32]byte, []rune) string
func stringtoslicebyte(*[32]byte, string) []byte func stringtoslicebyte(*[32]byte, string) []byte
func stringtoslicerune(*[32]rune, string) []rune func stringtoslicerune(*[32]rune, string) []rune
func slicecopy(toPtr *any, toLen int, frPtr *any, frLen int, wid uintptr) int func slicecopy(toPtr *any, toLen int, fromPtr *any, fromLen int, wid uintptr) int
func slicestringcopy(toPtr *byte, toLen int, fr string) int
func decoderune(string, int) (retv rune, retk int) func decoderune(string, int) (retv rune, retk int)
func countrunes(string) int func countrunes(string) int
@ -169,8 +168,8 @@ func selectnbsend(hchan chan<- any, elem *any) bool
func selectnbrecv(elem *any, hchan <-chan any) bool func selectnbrecv(elem *any, hchan <-chan any) bool
func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool
func selectsetpc(cas *byte) func selectsetpc(pc *uintptr)
func selectgo(cas0 *byte, order0 *byte, ncases int) (int, bool) func selectgo(cas0 *byte, order0 *byte, pc0 *uintptr, nsends int, nrecvs int, block bool) (int, bool)
func block() func block()
func makeslice(typ *byte, len int, cap int) unsafe.Pointer func makeslice(typ *byte, len int, cap int) unsafe.Pointer

View file

@ -107,8 +107,7 @@ func typecheckclosure(clo *Node, top int) {
} }
xfunc.Func.Nname.Sym = closurename(Curfn) xfunc.Func.Nname.Sym = closurename(Curfn)
disableExport(xfunc.Func.Nname.Sym) setNodeNameFunc(xfunc.Func.Nname)
declare(xfunc.Func.Nname, PFUNC)
xfunc = typecheck(xfunc, ctxStmt) xfunc = typecheck(xfunc, ctxStmt)
// Type check the body now, but only if we're inside a function. // Type check the body now, but only if we're inside a function.
@ -429,6 +428,7 @@ func typecheckpartialcall(fn *Node, sym *types.Sym) {
// Create top-level function. // Create top-level function.
xfunc := makepartialcall(fn, fn.Type, sym) xfunc := makepartialcall(fn, fn.Type, sym)
fn.Func = xfunc.Func fn.Func = xfunc.Func
fn.Func.SetWrapper(true)
fn.Right = newname(sym) fn.Right = newname(sym)
fn.Op = OCALLPART fn.Op = OCALLPART
fn.Type = xfunc.Type fn.Type = xfunc.Type
@ -462,7 +462,6 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
tfn.List.Set(structargs(t0.Params(), true)) tfn.List.Set(structargs(t0.Params(), true))
tfn.Rlist.Set(structargs(t0.Results(), false)) tfn.Rlist.Set(structargs(t0.Results(), false))
disableExport(sym)
xfunc := dclfunc(sym, tfn) xfunc := dclfunc(sym, tfn)
xfunc.Func.SetDupok(true) xfunc.Func.SetDupok(true)
xfunc.Func.SetNeedctxt(true) xfunc.Func.SetNeedctxt(true)
@ -525,7 +524,7 @@ func walkpartialcall(n *Node, init *Nodes) *Node {
// Create closure in the form of a composite literal. // Create closure in the form of a composite literal.
// For x.M with receiver (x) type T, the generated code looks like: // For x.M with receiver (x) type T, the generated code looks like:
// //
// clos = &struct{F uintptr; R T}{M.T·f, x} // clos = &struct{F uintptr; R T}{T.M·f, x}
// //
// Like walkclosure above. // Like walkclosure above.

View file

@ -44,7 +44,7 @@ func (v Val) Ctype() Ctype {
Fatalf("unexpected Ctype for %T", v.U) Fatalf("unexpected Ctype for %T", v.U)
panic("unreachable") panic("unreachable")
case nil: case nil:
return 0 return CTxxx
case *NilVal: case *NilVal:
return CTNIL return CTNIL
case bool: case bool:
@ -261,7 +261,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Nod
} }
if t == nil || !okforconst[t.Etype] { if t == nil || !okforconst[t.Etype] {
t = defaultType(idealkind(n)) t = defaultType(n.Type)
} }
switch n.Op { switch n.Op {
@ -838,10 +838,6 @@ Outer:
return Val{} return Val{}
} }
u.Quo(y) u.Quo(y)
case OMOD, OOR, OAND, OANDNOT, OXOR:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped float", op)
return Val{}
default: default:
break Outer break Outer
} }
@ -867,10 +863,6 @@ Outer:
yyerror("complex division by zero") yyerror("complex division by zero")
return Val{} return Val{}
} }
case OMOD, OOR, OAND, OANDNOT, OXOR:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped complex", op)
return Val{}
default: default:
break Outer break Outer
} }
@ -932,15 +924,6 @@ func unaryOp(op Op, x Val, t *types.Type) Val {
} }
u.Xor(x) u.Xor(x)
return Val{U: u} return Val{U: u}
case CTFLT:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped float", op)
return Val{}
case CTCPLX:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped complex", op)
return Val{}
} }
case ONOT: case ONOT:
@ -994,10 +977,8 @@ func setconst(n *Node, v Val) {
Xoffset: BADWIDTH, Xoffset: BADWIDTH,
} }
n.SetVal(v) n.SetVal(v)
if n.Type.IsUntyped() { if vt := idealType(v.Ctype()); n.Type.IsUntyped() && n.Type != vt {
// TODO(mdempsky): Make typecheck responsible for setting Fatalf("untyped type mismatch, have: %v, want: %v", n.Type, vt)
// the correct untyped type.
n.Type = idealType(v.Ctype())
} }
// Check range. // Check range.
@ -1056,67 +1037,6 @@ func idealType(ct Ctype) *types.Type {
return nil return nil
} }
// idealkind returns a constant kind like consttype
// but for an arbitrary "ideal" (untyped constant) expression.
func idealkind(n *Node) Ctype {
if n == nil || !n.Type.IsUntyped() {
return CTxxx
}
switch n.Op {
default:
return CTxxx
case OLITERAL:
return n.Val().Ctype()
// numeric kinds.
case OADD,
OAND,
OANDNOT,
OBITNOT,
ODIV,
ONEG,
OMOD,
OMUL,
OSUB,
OXOR,
OOR,
OPLUS:
k1 := idealkind(n.Left)
k2 := idealkind(n.Right)
if k1 > k2 {
return k1
} else {
return k2
}
case OREAL, OIMAG:
return CTFLT
case OCOMPLEX:
return CTCPLX
case OADDSTR:
return CTSTR
case OANDAND,
OEQ,
OGE,
OGT,
OLE,
OLT,
ONE,
ONOT,
OOROR:
return CTBOOL
// shifts (beware!).
case OLSH, ORSH:
return idealkind(n.Left)
}
}
// defaultlit on both nodes simultaneously; // defaultlit on both nodes simultaneously;
// if they're both ideal going in they better // if they're both ideal going in they better
// get the same type going out. // get the same type going out.
@ -1152,32 +1072,60 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) {
return l, r return l, r
} }
k := idealkind(l) t := defaultType(mixUntyped(l.Type, r.Type))
if rk := idealkind(r); rk > k {
k = rk
}
t := defaultType(k)
l = convlit(l, t) l = convlit(l, t)
r = convlit(r, t) r = convlit(r, t)
return l, r return l, r
} }
func defaultType(k Ctype) *types.Type { func ctype(t *types.Type) Ctype {
switch k { switch t {
case CTBOOL: case types.Idealbool:
return CTBOOL
case types.Idealstring:
return CTSTR
case types.Idealint:
return CTINT
case types.Idealrune:
return CTRUNE
case types.Idealfloat:
return CTFLT
case types.Idealcomplex:
return CTCPLX
}
Fatalf("bad type %v", t)
panic("unreachable")
}
func mixUntyped(t1, t2 *types.Type) *types.Type {
t := t1
if ctype(t2) > ctype(t1) {
t = t2
}
return t
}
func defaultType(t *types.Type) *types.Type {
if !t.IsUntyped() || t.Etype == TNIL {
return t
}
switch t {
case types.Idealbool:
return types.Types[TBOOL] return types.Types[TBOOL]
case CTSTR: case types.Idealstring:
return types.Types[TSTRING] return types.Types[TSTRING]
case CTINT: case types.Idealint:
return types.Types[TINT] return types.Types[TINT]
case CTRUNE: case types.Idealrune:
return types.Runetype return types.Runetype
case CTFLT: case types.Idealfloat:
return types.Types[TFLOAT64] return types.Types[TFLOAT64]
case CTCPLX: case types.Idealcomplex:
return types.Types[TCOMPLEX128] return types.Types[TCOMPLEX128]
} }
Fatalf("bad idealkind: %v", k)
Fatalf("bad type %v", t)
return nil return nil
} }

View file

@ -90,7 +90,7 @@ func declare(n *Node, ctxt Class) {
lineno = n.Pos lineno = n.Pos
Fatalf("automatic outside function") Fatalf("automatic outside function")
} }
if Curfn != nil { if Curfn != nil && ctxt != PFUNC {
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
} }
if n.Op == OTYPE { if n.Op == OTYPE {
@ -297,6 +297,16 @@ func oldname(s *types.Sym) *Node {
return n return n
} }
// importName is like oldname, but it reports an error if sym is from another package and not exported.
func importName(sym *types.Sym) *Node {
n := oldname(sym)
if !types.IsExported(sym.Name) && sym.Pkg != localpkg {
n.SetDiag(true)
yyerror("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
}
return n
}
// := declarations // := declarations
func colasname(n *Node) bool { func colasname(n *Node) bool {
switch n.Op { switch n.Op {
@ -372,14 +382,11 @@ func ifacedcl(n *Node) {
// returns in auto-declaration context. // returns in auto-declaration context.
func funchdr(n *Node) { func funchdr(n *Node) {
// change the declaration context from extern to auto // change the declaration context from extern to auto
if Curfn == nil && dclcontext != PEXTERN { funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext})
Fatalf("funchdr: dclcontext = %d", dclcontext)
}
dclcontext = PAUTO
types.Markdcl()
funcstack = append(funcstack, Curfn)
Curfn = n Curfn = n
dclcontext = PAUTO
types.Markdcl()
if n.Func.Nname != nil { if n.Func.Nname != nil {
funcargs(n.Func.Nname.Name.Param.Ntype) funcargs(n.Func.Nname.Name.Param.Ntype)
@ -487,21 +494,22 @@ func funcarg2(f *types.Field, ctxt Class) {
declare(n, ctxt) declare(n, ctxt)
} }
var funcstack []*Node // stack of previous values of Curfn var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext
type funcStackEnt struct {
curfn *Node
dclcontext Class
}
// 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.
func funcbody() { func funcbody() {
// change the declaration context from auto to extern // change the declaration context from auto to previous context
if dclcontext != PAUTO {
Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
}
types.Popdcl() types.Popdcl()
funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1] var e funcStackEnt
if Curfn == nil { funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
dclcontext = PEXTERN Curfn, dclcontext = e.curfn, e.dclcontext
}
} }
// structs, functions, and methods. // structs, functions, and methods.
@ -975,10 +983,14 @@ func makefuncsym(s *types.Sym) {
} }
} }
// disableExport prevents sym from being included in package export // setNodeNameFunc marks a node as a function.
// data. To be effectual, it must be called before declare. func setNodeNameFunc(n *Node) {
func disableExport(sym *types.Sym) { if n.Op != ONAME || n.Class() != Pxxx {
sym.SetOnExportList(true) Fatalf("expected ONAME/Pxxx node, got %v", n)
}
n.SetClass(PFUNC)
n.Sym.SetFunc(true)
} }
func dclfunc(sym *types.Sym, tfn *Node) *Node { func dclfunc(sym *types.Sym, tfn *Node) *Node {
@ -990,7 +1002,7 @@ func dclfunc(sym *types.Sym, tfn *Node) *Node {
fn.Func.Nname = newfuncnamel(lineno, sym) fn.Func.Nname = newfuncnamel(lineno, sym)
fn.Func.Nname.Name.Defn = fn fn.Func.Nname.Name.Defn = fn
fn.Func.Nname.Name.Param.Ntype = tfn fn.Func.Nname.Name.Param.Ntype = tfn
declare(fn.Func.Nname, PFUNC) setNodeNameFunc(fn.Func.Nname)
funchdr(fn) funchdr(fn)
fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, ctxType) fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, ctxType)
return fn return fn

View file

@ -187,6 +187,13 @@ func mustHeapAlloc(n *Node) bool {
return true return true
} }
if n.Op == OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
return true
}
if n.Op == OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize {
return true
}
if n.Op == OMAKESLICE && !isSmallMakeSlice(n) { if n.Op == OMAKESLICE && !isSmallMakeSlice(n) {
return true return true
} }
@ -370,14 +377,14 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// This really doesn't have much to do with escape analysis per se, // This really doesn't have much to do with escape analysis per se,
// but we are reusing the ability to annotate an individual function // but we are reusing the ability to annotate an individual function
// argument and pass those annotations along to importing code. // argument and pass those annotations along to importing code.
if f.Type.Etype == TUINTPTR { if f.Type.IsUintptr() {
if Debug['m'] != 0 { if Debug['m'] != 0 {
Warnl(f.Pos, "assuming %v is unsafe uintptr", name()) Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
} }
return unsafeUintptrTag return unsafeUintptrTag
} }
if !types.Haspointers(f.Type) { // don't bother tagging for scalars if !f.Type.HasPointers() { // don't bother tagging for scalars
return "" return ""
} }
@ -400,13 +407,13 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
} }
if fn.Func.Pragma&UintptrEscapes != 0 { if fn.Func.Pragma&UintptrEscapes != 0 {
if f.Type.Etype == TUINTPTR { if f.Type.IsUintptr() {
if Debug['m'] != 0 { if Debug['m'] != 0 {
Warnl(f.Pos, "marking %v as escaping uintptr", name()) Warnl(f.Pos, "marking %v as escaping uintptr", name())
} }
return uintptrEscapesTag return uintptrEscapesTag
} }
if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR { if f.IsDDD() && f.Type.Elem().IsUintptr() {
// final argument is ...uintptr. // final argument is ...uintptr.
if Debug['m'] != 0 { if Debug['m'] != 0 {
Warnl(f.Pos, "marking %v as escaping ...uintptr", name()) Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
@ -415,7 +422,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
} }
} }
if !types.Haspointers(f.Type) { // don't bother tagging for scalars if !f.Type.HasPointers() { // don't bother tagging for scalars
return "" return ""
} }

View file

@ -326,7 +326,7 @@ func (e *Escape) stmt(n *Node) {
if typesw && n.Left.Left != nil { if typesw && n.Left.Left != nil {
cv := cas.Rlist.First() cv := cas.Rlist.First()
k := e.dcl(cv) // type switch variables have no ODCL. k := e.dcl(cv) // type switch variables have no ODCL.
if types.Haspointers(cv.Type) { if cv.Type.HasPointers() {
ks = append(ks, k.dotType(cv.Type, cas, "switch case")) ks = append(ks, k.dotType(cv.Type, cas, "switch case"))
} }
} }
@ -433,7 +433,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
if uintptrEscapesHack && n.Op == OCONVNOP && n.Left.Type.IsUnsafePtr() { if uintptrEscapesHack && n.Op == OCONVNOP && n.Left.Type.IsUnsafePtr() {
// nop // nop
} else if k.derefs >= 0 && !types.Haspointers(n.Type) { } else if k.derefs >= 0 && !n.Type.HasPointers() {
k = e.discardHole() k = e.discardHole()
} }
@ -485,7 +485,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
e.discard(max) e.discard(max)
case OCONV, OCONVNOP: case OCONV, OCONVNOP:
if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() { if checkPtr(e.curfn, 2) && n.Type.IsUnsafePtr() && n.Left.Type.IsPtr() {
// When -d=checkptr=2 is enabled, treat // When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an // conversions to unsafe.Pointer as an
// escaping operation. This allows better // escaping operation. This allows better
@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
// easily detect object boundaries on the heap // easily detect object boundaries on the heap
// than the stack. // than the stack.
e.assignHeap(n.Left, "conversion to unsafe.Pointer", n) e.assignHeap(n.Left, "conversion to unsafe.Pointer", n)
} else if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { } else if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() {
e.unsafeValue(k, n.Left) e.unsafeValue(k, n.Left)
} else { } else {
e.expr(k, n.Left) e.expr(k, n.Left)
@ -625,7 +625,7 @@ func (e *Escape) unsafeValue(k EscHole, n *Node) {
switch n.Op { switch n.Op {
case OCONV, OCONVNOP: case OCONV, OCONVNOP:
if n.Left.Type.Etype == TUNSAFEPTR { if n.Left.Type.IsUnsafePtr() {
e.expr(k, n.Left) e.expr(k, n.Left)
} else { } else {
e.discard(n.Left) e.discard(n.Left)
@ -698,7 +698,7 @@ func (e *Escape) addr(n *Node) EscHole {
e.assignHeap(n.Right, "key of map put", n) e.assignHeap(n.Right, "key of map put", n)
} }
if !types.Haspointers(n.Type) { if !n.Type.HasPointers() {
k = e.discardHole() k = e.discardHole()
} }
@ -811,14 +811,14 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
// slice might be allocated, and all slice elements // slice might be allocated, and all slice elements
// might flow to heap. // might flow to heap.
appendeeK := ks[0] appendeeK := ks[0]
if types.Haspointers(args[0].Type.Elem()) { if args[0].Type.Elem().HasPointers() {
appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice")) appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
} }
argument(appendeeK, args[0]) argument(appendeeK, args[0])
if call.IsDDD() { if call.IsDDD() {
appendedK := e.discardHole() appendedK := e.discardHole()
if args[1].Type.IsSlice() && types.Haspointers(args[1].Type.Elem()) { if args[1].Type.IsSlice() && args[1].Type.Elem().HasPointers() {
appendedK = e.heapHole().deref(call, "appended slice...") appendedK = e.heapHole().deref(call, "appended slice...")
} }
argument(appendedK, args[1]) argument(appendedK, args[1])
@ -832,7 +832,7 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
argument(e.discardHole(), call.Left) argument(e.discardHole(), call.Left)
copiedK := e.discardHole() copiedK := e.discardHole()
if call.Right.Type.IsSlice() && types.Haspointers(call.Right.Type.Elem()) { if call.Right.Type.IsSlice() && call.Right.Type.Elem().HasPointers() {
copiedK = e.heapHole().deref(call, "copied slice") copiedK = e.heapHole().deref(call, "copied slice")
} }
argument(copiedK, call.Right) argument(copiedK, call.Right)
@ -1029,6 +1029,9 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
if e.curfn == nil { if e.curfn == nil {
Fatalf("e.curfn isn't set") Fatalf("e.curfn isn't set")
} }
if n != nil && n.Type != nil && n.Type.NotInHeap() {
yyerrorl(n.Pos, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type)
}
n = canonicalNode(n) n = canonicalNode(n)
loc := &EscLocation{ loc := &EscLocation{

View file

@ -711,6 +711,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
return return
} }
if t.Etype == types.TRESULTS {
tys := t.Extra.(*types.Results).Types
for i, et := range tys {
if i > 0 {
b.WriteByte(',')
}
b.WriteString(et.String())
}
return
}
flag, mode = flag.update(mode) flag, mode = flag.update(mode)
if mode == FTypeIdName { if mode == FTypeIdName {
flag |= FmtUnsigned flag |= FmtUnsigned
@ -1407,7 +1418,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
return return
} }
if n.Right != nil { if n.Right != nil {
mode.Fprintf(s, "%v literal", n.Right) mode.Fprintf(s, "%v{%s}", n.Right, ellipsisIf(n.List.Len() != 0))
return return
} }
@ -1421,7 +1432,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT: case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
if mode == FErr { if mode == FErr {
mode.Fprintf(s, "%v literal", n.Type) mode.Fprintf(s, "%v{%s}", n.Type, ellipsisIf(n.List.Len() != 0))
return return
} }
mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List) mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List)
@ -1616,7 +1627,8 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
} }
n1.exprfmt(s, nprec, mode) n1.exprfmt(s, nprec, mode)
} }
case ODDD:
mode.Fprintf(s, "...")
default: default:
mode.Fprintf(s, "<node %v>", n.Op) mode.Fprintf(s, "<node %v>", n.Op)
} }
@ -1933,3 +1945,10 @@ func indent(s fmt.State) {
fmt.Fprint(s, ". ") fmt.Fprint(s, ". ")
} }
} }
func ellipsisIf(b bool) string {
if b {
return "..."
}
return ""
}

View file

@ -32,7 +32,6 @@ package gc
import ( import (
"cmd/compile/internal/ssa" "cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/objabi" "cmd/internal/objabi"
"cmd/internal/src" "cmd/internal/src"
@ -316,7 +315,7 @@ func ggloblnod(nam *Node) {
if nam.Name.Readonly() { if nam.Name.Readonly() {
flags = obj.RODATA flags = obj.RODATA
} }
if nam.Type != nil && !types.Haspointers(nam.Type) { if nam.Type != nil && !nam.Type.HasPointers() {
flags |= obj.NOPTR flags |= obj.NOPTR
} }
Ctxt.Globl(s, nam.Type.Width, flags) Ctxt.Globl(s, nam.Type.Width, flags)
@ -343,6 +342,6 @@ func Patch(p *obj.Prog, to *obj.Prog) {
if p.To.Type != obj.TYPE_BRANCH { if p.To.Type != obj.TYPE_BRANCH {
Fatalf("patch: not a branch") Fatalf("patch: not a branch")
} }
p.To.Val = to p.To.SetTarget(to)
p.To.Offset = to.Pc p.To.Offset = to.Pc
} }

View file

@ -205,8 +205,9 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/goobj2" "cmd/internal/goobj"
"cmd/internal/src" "cmd/internal/src"
"crypto/md5"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
@ -295,12 +296,15 @@ func iexport(out *bufio.Writer) {
hdr.uint64(dataLen) hdr.uint64(dataLen)
// Flush output. // Flush output.
io.Copy(out, &hdr) h := md5.New()
io.Copy(out, &p.strings) wr := io.MultiWriter(out, h)
io.Copy(out, &p.data0) io.Copy(wr, &hdr)
io.Copy(wr, &p.strings)
io.Copy(wr, &p.data0)
// Add fingerprint (used by linker object file). // Add fingerprint (used by linker object file).
// Attach this to the end, so tools (e.g. gcimporter) don't care. // Attach this to the end, so tools (e.g. gcimporter) don't care.
copy(Ctxt.Fingerprint[:], h.Sum(nil)[:])
out.Write(Ctxt.Fingerprint[:]) out.Write(Ctxt.Fingerprint[:])
} }
@ -480,6 +484,7 @@ func (p *iexporter) doDecl(n *Node) {
t := n.Type t := n.Type
if t.IsInterface() { if t.IsInterface() {
w.typeExt(t)
break break
} }
@ -492,6 +497,7 @@ func (p *iexporter) doDecl(n *Node) {
w.signature(m.Type) w.signature(m.Type)
} }
w.typeExt(t)
for _, m := range ms.Slice() { for _, m := range ms.Slice() {
w.methExt(m) w.methExt(m)
} }
@ -997,21 +1003,30 @@ func (w *exportWriter) linkname(s *types.Sym) {
} }
func (w *exportWriter) symIdx(s *types.Sym) { func (w *exportWriter) symIdx(s *types.Sym) {
if Ctxt.Flag_go115newobj { lsym := s.Linksym()
lsym := s.Linksym() if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" { // Don't export index for non-package symbols, linkname'd symbols,
// Don't export index for non-package symbols, linkname'd symbols, // and symbols without an index. They can only be referenced by
// and symbols without an index. They can only be referenced by // name.
// name. w.int64(-1)
w.int64(-1) } else {
} else { // For a defined symbol, export its index.
// For a defined symbol, export its index. // For re-exporting an imported symbol, pass its index through.
// For re-exporting an imported symbol, pass its index through. w.int64(int64(lsym.SymIdx))
w.int64(int64(lsym.SymIdx))
}
} }
} }
func (w *exportWriter) typeExt(t *types.Type) {
// For type T, export the index of type descriptor symbols of T and *T.
if i, ok := typeSymIdx[t]; ok {
w.int64(i[0])
w.int64(i[1])
return
}
w.symIdx(typesym(t))
w.symIdx(typesym(t.PtrTo()))
}
// Inline bodies. // Inline bodies.
func (w *exportWriter) stmtList(list Nodes) { func (w *exportWriter) stmtList(list Nodes) {

View file

@ -10,7 +10,7 @@ package gc
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/bio" "cmd/internal/bio"
"cmd/internal/goobj2" "cmd/internal/goobj"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/src" "cmd/internal/src"
"encoding/binary" "encoding/binary"
@ -97,7 +97,7 @@ func (r *intReader) uint64() uint64 {
return i return i
} }
func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj2.FingerprintType) { func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) {
ir := &intReader{in, pkg} ir := &intReader{in, pkg}
version := ir.uint64() version := ir.uint64()
@ -191,9 +191,9 @@ func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj2.FingerprintType
} }
} }
// Fingerprint // Fingerprint.
n, err := io.ReadFull(in, fingerprint[:]) _, err = io.ReadFull(in, fingerprint[:])
if err != nil || n != len(fingerprint) { if err != nil {
yyerror("import %s: error reading fingerprint", pkg.Path) yyerror("import %s: error reading fingerprint", pkg.Path)
errorexit() errorexit()
} }
@ -316,6 +316,7 @@ func (r *importReader) doDecl(n *Node) {
resumecheckwidth() resumecheckwidth()
if underlying.IsInterface() { if underlying.IsInterface() {
r.typeExt(t)
break break
} }
@ -346,6 +347,7 @@ func (r *importReader) doDecl(n *Node) {
} }
t.Methods().Set(ms) t.Methods().Set(ms)
r.typeExt(t)
for _, m := range ms { for _, m := range ms {
r.methExt(m) r.methExt(m)
} }
@ -697,19 +699,28 @@ func (r *importReader) linkname(s *types.Sym) {
} }
func (r *importReader) symIdx(s *types.Sym) { func (r *importReader) symIdx(s *types.Sym) {
if Ctxt.Flag_go115newobj { lsym := s.Linksym()
lsym := s.Linksym() idx := int32(r.int64())
idx := int32(r.int64()) if idx != -1 {
if idx != -1 { if s.Linkname != "" {
if s.Linkname != "" { Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
}
lsym.SymIdx = idx
lsym.Set(obj.AttrIndexed, true)
} }
lsym.SymIdx = idx
lsym.Set(obj.AttrIndexed, true)
} }
} }
func (r *importReader) typeExt(t *types.Type) {
i, pi := r.int64(), r.int64()
if i != -1 && pi != -1 {
typeSymIdx[t] = [2]int64{i, pi}
}
}
// Map imported type T to the index of type descriptor symbols of T and *T,
// so we can use index to reference the symbol.
var typeSymIdx = make(map[*types.Type][2]int64)
func (r *importReader) doInline(n *Node) { func (r *importReader) doInline(n *Node) {
if len(n.Func.Inl.Body) != 0 { if len(n.Func.Inl.Body) != 0 {
Fatalf("%v already has inline body", n) Fatalf("%v already has inline body", n)

View file

@ -45,7 +45,6 @@ func fninit(n []*Node) {
if len(nf) > 0 { if len(nf) > 0 {
lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
initializers := lookup("init") initializers := lookup("init")
disableExport(initializers)
fn := dclfunc(initializers, nod(OTFUNC, nil, nil)) fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl { for _, dcl := range dummyInitFn.Func.Dcl {
dcl.Name.Curfn = fn dcl.Name.Curfn = fn
@ -60,7 +59,7 @@ func fninit(n []*Node) {
Curfn = fn Curfn = fn
typecheckslice(nf, ctxStmt) typecheckslice(nf, ctxStmt)
Curfn = nil Curfn = nil
funccompile(fn) xtop = append(xtop, fn)
fns = append(fns, initializers.Linksym()) fns = append(fns, initializers.Linksym())
} }
if dummyInitFn.Func.Dcl != nil { if dummyInitFn.Func.Dcl != nil {
@ -69,16 +68,14 @@ func fninit(n []*Node) {
// something's weird if we get here. // something's weird if we get here.
Fatalf("dummyInitFn still has declarations") Fatalf("dummyInitFn still has declarations")
} }
dummyInitFn = nil
// Record user init functions. // Record user init functions.
for i := 0; i < renameinitgen; i++ { for i := 0; i < renameinitgen; i++ {
s := lookupN("init.", i) s := lookupN("init.", i)
fn := asNode(s.Def).Name.Defn fn := asNode(s.Def).Name.Defn
// Skip init functions with empty bodies. // Skip init functions with empty bodies.
// noder.go doesn't allow external init functions, and if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY {
// order.go has already removed any OEMPTY nodes, so
// checking Len() == 0 is sufficient here.
if fn.Nbody.Len() == 0 {
continue continue
} }
fns = append(fns, s.Linksym()) fns = append(fns, s.Linksym())

View file

@ -14,7 +14,7 @@ import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/bio" "cmd/internal/bio"
"cmd/internal/dwarf" "cmd/internal/dwarf"
"cmd/internal/goobj2" "cmd/internal/goobj"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/objabi" "cmd/internal/objabi"
"cmd/internal/src" "cmd/internal/src"
@ -281,11 +281,12 @@ func Main(archInit func(*Arch)) {
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`") flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects") flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF") flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
flag.BoolVar(&Ctxt.Flag_go115newobj, "go115newobj", true, "use new object file format")
flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging") flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging")
objabi.Flagparse(usage) objabi.Flagparse(usage)
Ctxt.Pkgpath = myimportpath
for _, f := range strings.Split(spectre, ",") { for _, f := range strings.Split(spectre, ",") {
f = strings.TrimSpace(f) f = strings.TrimSpace(f)
switch f { switch f {
@ -315,7 +316,7 @@ func Main(archInit func(*Arch)) {
// Record flags that affect the build result. (And don't // Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious // record flags that don't, since that would cause spurious
// changes in the binary.) // changes in the binary.)
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre", "go115newobj") recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
if smallFrames { if smallFrames {
maxStackVarSize = 128 * 1024 maxStackVarSize = 128 * 1024
@ -616,7 +617,7 @@ func Main(archInit func(*Arch)) {
var fcount int64 var fcount int64
for i := 0; i < len(xtop); i++ { for i := 0; i < len(xtop); i++ {
n := xtop[i] n := xtop[i]
if op := n.Op; op == ODCLFUNC || op == OCLOSURE { if n.Op == ODCLFUNC {
Curfn = n Curfn = n
decldepth = 1 decldepth = 1
saveerrors() saveerrors()
@ -641,6 +642,8 @@ func Main(archInit func(*Arch)) {
errorexit() errorexit()
} }
fninit(xtop)
// Phase 4: Decide how to capture closed variables. // Phase 4: Decide how to capture closed variables.
// This needs to run before escape analysis, // This needs to run before escape analysis,
// because variables captured by value do not escape. // because variables captured by value do not escape.
@ -750,10 +753,6 @@ func Main(archInit func(*Arch)) {
} }
timings.AddEvent(fcount, "funcs") timings.AddEvent(fcount, "funcs")
if nsavederrors+nerrors == 0 {
fninit(xtop)
}
compileFunctions() compileFunctions()
if nowritebarrierrecCheck != nil { if nowritebarrierrecCheck != nil {
@ -790,7 +789,7 @@ func Main(archInit func(*Arch)) {
// Write object data to disk. // Write object data to disk.
timings.Start("be", "dumpobj") timings.Start("be", "dumpobj")
dumpdata() dumpdata()
Ctxt.NumberSyms(false) Ctxt.NumberSyms()
dumpobj() dumpobj()
if asmhdr != "" { if asmhdr != "" {
dumpasmhdr() dumpasmhdr()
@ -808,6 +807,9 @@ func Main(archInit func(*Arch)) {
} }
} }
if len(funcStack) != 0 {
Fatalf("funcStack is non-empty: %v", len(funcStack))
}
if len(compilequeue) != 0 { if len(compilequeue) != 0 {
Fatalf("%d uncompiled functions", len(compilequeue)) Fatalf("%d uncompiled functions", len(compilequeue))
} }
@ -1279,7 +1281,7 @@ func importfile(f *Val) *types.Pkg {
c, _ = imp.ReadByte() c, _ = imp.ReadByte()
} }
var fingerprint goobj2.FingerprintType var fingerprint goobj.FingerprintType
switch c { switch c {
case '\n': case '\n':
yyerror("cannot import %s: old export format no longer supported (recompile library)", path_) yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
@ -1489,7 +1491,7 @@ func recordFlags(flags ...string) {
return return
} }
s := Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + myimportpath) s := Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + myimportpath)
s.Type = objabi.SDWARFINFO s.Type = objabi.SDWARFCUINFO
// Sometimes (for example when building tests) we can link // Sometimes (for example when building tests) we can link
// together two package main archives. So allow dups. // together two package main archives. So allow dups.
s.Set(obj.AttrDuplicateOK, true) s.Set(obj.AttrDuplicateOK, true)
@ -1501,7 +1503,7 @@ func recordFlags(flags ...string) {
// compiled, so that the linker can save it in the compile unit's DIE. // compiled, so that the linker can save it in the compile unit's DIE.
func recordPackageName() { func recordPackageName() {
s := Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + myimportpath) s := Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + myimportpath)
s.Type = objabi.SDWARFINFO s.Type = objabi.SDWARFCUINFO
// Sometimes (for example when building tests) we can link // Sometimes (for example when building tests) we can link
// together two package main archives. So allow dups. // together two package main archives. So allow dups.
s.Set(obj.AttrDuplicateOK, true) s.Set(obj.AttrDuplicateOK, true)

View file

@ -653,7 +653,7 @@ func (p *noder) expr(expr syntax.Expr) *Node {
obj := p.expr(expr.X) obj := p.expr(expr.X)
if obj.Op == OPACK { if obj.Op == OPACK {
obj.Name.SetUsed(true) obj.Name.SetUsed(true)
return oldname(restrictlookup(expr.Sel.Value, obj.Name.Pkg)) return importName(obj.Name.Pkg.Lookup(expr.Sel.Value))
} }
n := nodSym(OXDOT, obj, p.name(expr.Sel)) n := nodSym(OXDOT, obj, p.name(expr.Sel))
n.Pos = p.pos(expr) // lineno may have been changed by p.expr(expr.X) n.Pos = p.pos(expr) // lineno may have been changed by p.expr(expr.X)
@ -857,7 +857,7 @@ func (p *noder) interfaceType(expr *syntax.InterfaceType) *Node {
p.setlineno(method) p.setlineno(method)
var n *Node var n *Node
if method.Name == nil { if method.Name == nil {
n = p.nodSym(method, ODCLFIELD, oldname(p.packname(method.Type)), nil) n = p.nodSym(method, ODCLFIELD, importName(p.packname(method.Type)), nil)
} else { } else {
mname := p.name(method.Name) mname := p.name(method.Name)
sig := p.typeExpr(method.Type) sig := p.typeExpr(method.Type)
@ -896,7 +896,7 @@ func (p *noder) packname(expr syntax.Expr) *types.Sym {
def.Name.SetUsed(true) def.Name.SetUsed(true)
pkg = def.Name.Pkg pkg = def.Name.Pkg
} }
return restrictlookup(expr.Sel.Value, pkg) return pkg.Lookup(expr.Sel.Value)
} }
panic(fmt.Sprintf("unexpected packname: %#v", expr)) panic(fmt.Sprintf("unexpected packname: %#v", expr))
} }
@ -911,7 +911,7 @@ func (p *noder) embedded(typ syntax.Expr) *Node {
} }
sym := p.packname(typ) sym := p.packname(typ)
n := p.nodSym(typ, ODCLFIELD, oldname(sym), lookup(sym.Name)) n := p.nodSym(typ, ODCLFIELD, importName(sym), lookup(sym.Name))
n.SetEmbedded(true) n.SetEmbedded(true)
if isStar { if isStar {
@ -1641,10 +1641,3 @@ func mkname(sym *types.Sym) *Node {
} }
return n return n
} }
func unparen(x *Node) *Node {
for x.Op == OPAREN {
x = x.Left
}
return x
}

View file

@ -113,12 +113,16 @@ func dumpCompilerObj(bout *bio.Writer) {
func dumpdata() { func dumpdata() {
externs := len(externdcl) externs := len(externdcl)
xtops := len(xtop)
dumpglobls() dumpglobls()
addptabs() addptabs()
exportlistLen := len(exportlist)
addsignats(externdcl) addsignats(externdcl)
dumpsignats() dumpsignats()
dumptabs() dumptabs()
ptabsLen := len(ptabs)
itabsLen := len(itabs)
dumpimportstrings() dumpimportstrings()
dumpbasictypes() dumpbasictypes()
@ -129,9 +133,19 @@ func dumpdata() {
// number of types in a finite amount of code. // number of types in a finite amount of code.
// In the typical case, we loop 0 or 1 times. // In the typical case, we loop 0 or 1 times.
// It was not until issue 24761 that we found any code that required a loop at all. // It was not until issue 24761 that we found any code that required a loop at all.
for len(compilequeue) > 0 { for {
for i := xtops; i < len(xtop); i++ {
n := xtop[i]
if n.Op == ODCLFUNC {
funccompile(n)
}
}
xtops = len(xtop)
compileFunctions() compileFunctions()
dumpsignats() dumpsignats()
if xtops == len(xtop) {
break
}
} }
// Dump extra globals. // Dump extra globals.
@ -149,6 +163,16 @@ func dumpdata() {
} }
addGCLocals() addGCLocals()
if exportlistLen != len(exportlist) {
Fatalf("exportlist changed after compile functions loop")
}
if ptabsLen != len(ptabs) {
Fatalf("ptabs changed after compile functions loop")
}
if itabsLen != len(itabs) {
Fatalf("itabs changed after compile functions loop")
}
} }
func dumpLinkerObj(bout *bio.Writer) { func dumpLinkerObj(bout *bio.Writer) {
@ -166,7 +190,7 @@ func dumpLinkerObj(bout *bio.Writer) {
fmt.Fprintf(bout, "\n!\n") fmt.Fprintf(bout, "\n!\n")
obj.WriteObjFile(Ctxt, bout, myimportpath) obj.WriteObjFile(Ctxt, bout)
} }
func addptabs() { func addptabs() {
@ -291,10 +315,8 @@ func addGCLocals() {
} }
if x := s.Func.StackObjects; x != nil { if x := s.Func.StackObjects; x != nil {
attr := int16(obj.RODATA) attr := int16(obj.RODATA)
if s.DuplicateOK() {
attr |= obj.DUPOK
}
ggloblsym(x, int32(len(x.P)), attr) ggloblsym(x, int32(len(x.P)), attr)
x.Set(obj.AttrStatic, true)
} }
if x := s.Func.OpenCodedDeferInfo; x != nil { if x := s.Func.OpenCodedDeferInfo; x != nil {
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
@ -354,10 +376,11 @@ func stringsym(pos src.XPos, s string) (data *obj.LSym) {
symdata := Ctxt.Lookup(symdataname) symdata := Ctxt.Lookup(symdataname)
if !symdata.SeenGlobl() { if !symdata.OnList() {
// string data // string data
off := dsname(symdata, 0, s, pos, "string") off := dsname(symdata, 0, s, pos, "string")
ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
symdata.Set(obj.AttrContentAddressable, true)
} }
return symdata return symdata

View file

@ -206,8 +206,7 @@ func (o *Order) addrTemp(n *Node) *Node {
// TODO: expand this to all static composite literal nodes? // TODO: expand this to all static composite literal nodes?
n = defaultlit(n, nil) n = defaultlit(n, nil)
dowidth(n.Type) dowidth(n.Type)
vstat := staticname(n.Type) vstat := readonlystaticname(n.Type)
vstat.MarkReadonly()
var s InitSchedule var s InitSchedule
s.staticassign(vstat, n) s.staticassign(vstat, n)
if s.out != nil { if s.out != nil {
@ -289,20 +288,13 @@ func (o *Order) popTemp(mark ordermarker) {
o.temp = o.temp[:mark] o.temp = o.temp[:mark]
} }
// cleanTempNoPop emits VARKILL and if needed VARLIVE instructions // cleanTempNoPop emits VARKILL instructions to *out
// to *out for each temporary above the mark on the temporary stack. // for each temporary above the mark on the temporary stack.
// It does not pop the temporaries from the stack. // It does not pop the temporaries from the stack.
func (o *Order) cleanTempNoPop(mark ordermarker) []*Node { func (o *Order) cleanTempNoPop(mark ordermarker) []*Node {
var out []*Node var out []*Node
for i := len(o.temp) - 1; i >= int(mark); i-- { for i := len(o.temp) - 1; i >= int(mark); i-- {
n := o.temp[i] n := o.temp[i]
if n.Name.Keepalive() {
n.Name.SetKeepalive(false)
n.Name.SetAddrtaken(true) // ensure SSA keeps the n variable
live := nod(OVARLIVE, n, nil)
live = typecheck(live, ctxStmt)
out = append(out, live)
}
kill := nod(OVARKILL, n, nil) kill := nod(OVARKILL, n, nil)
kill = typecheck(kill, ctxStmt) kill = typecheck(kill, ctxStmt)
out = append(out, kill) out = append(out, kill)
@ -501,8 +493,9 @@ func (o *Order) call(n *Node) {
// still alive when we pop the temp stack. // still alive when we pop the temp stack.
if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() { if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() {
x := o.copyExpr(arg.Left, arg.Left.Type, false) x := o.copyExpr(arg.Left, arg.Left.Type, false)
x.Name.SetKeepalive(true)
arg.Left = x arg.Left = x
x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable
n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt))
} }
} }
@ -928,7 +921,7 @@ func (o *Order) stmt(n *Node) {
n2.Ninit.Append(tmp2) n2.Ninit.Append(tmp2)
} }
r.Left = o.newTemp(r.Right.Left.Type.Elem(), types.Haspointers(r.Right.Left.Type.Elem())) r.Left = o.newTemp(r.Right.Left.Type.Elem(), r.Right.Left.Type.Elem().HasPointers())
tmp2 := nod(OAS, tmp1, r.Left) tmp2 := nod(OAS, tmp1, r.Left)
tmp2 = typecheck(tmp2, ctxStmt) tmp2 = typecheck(tmp2, ctxStmt)
n2.Ninit.Append(tmp2) n2.Ninit.Append(tmp2)
@ -1407,7 +1400,7 @@ func (o *Order) as2(n *Node) {
left := []*Node{} left := []*Node{}
for ni, l := range n.List.Slice() { for ni, l := range n.List.Slice() {
if !l.isBlank() { if !l.isBlank() {
tmp := o.newTemp(l.Type, types.Haspointers(l.Type)) tmp := o.newTemp(l.Type, l.Type.HasPointers())
n.List.SetIndex(ni, tmp) n.List.SetIndex(ni, tmp)
tmplist = append(tmplist, tmp) tmplist = append(tmplist, tmp)
left = append(left, l) left = append(left, l)
@ -1429,7 +1422,7 @@ func (o *Order) okAs2(n *Node) {
var tmp1, tmp2 *Node var tmp1, tmp2 *Node
if !n.List.First().isBlank() { if !n.List.First().isBlank() {
typ := n.Right.Type typ := n.Right.Type
tmp1 = o.newTemp(typ, types.Haspointers(typ)) tmp1 = o.newTemp(typ, typ.HasPointers())
} }
if !n.List.Second().isBlank() { if !n.List.Second().isBlank() {

View file

@ -80,8 +80,8 @@ func cmpstackvarlt(a, b *Node) bool {
return a.Name.Used() return a.Name.Used()
} }
ap := types.Haspointers(a.Type) ap := a.Type.HasPointers()
bp := types.Haspointers(b.Type) bp := b.Type.HasPointers()
if ap != bp { if ap != bp {
return ap return ap
} }
@ -176,7 +176,7 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
} }
s.stksize += w s.stksize += w
s.stksize = Rnd(s.stksize, int64(n.Type.Align)) s.stksize = Rnd(s.stksize, int64(n.Type.Align))
if types.Haspointers(n.Type) { if n.Type.HasPointers() {
s.stkptrsize = s.stksize s.stkptrsize = s.stksize
lastHasPtr = true lastHasPtr = true
} else { } else {
@ -428,9 +428,10 @@ func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.S
decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls) decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls)
// For each type referenced by the functions auto vars, attach a // For each type referenced by the functions auto vars but not
// dummy relocation to the function symbol to insure that the type // already referenced by a dwarf var, attach a dummy relocation to
// included in DWARF processing during linking. // the function symbol to insure that the type included in DWARF
// processing during linking.
typesyms := []*obj.LSym{} typesyms := []*obj.LSym{}
for t, _ := range fnsym.Func.Autot { for t, _ := range fnsym.Func.Autot {
typesyms = append(typesyms, t) typesyms = append(typesyms, t)
@ -480,7 +481,7 @@ func declPos(decl *Node) src.XPos {
// createSimpleVars creates a DWARF entry for every variable declared in the // createSimpleVars creates a DWARF entry for every variable declared in the
// function, claiming that they are permanently on the stack. // function, claiming that they are permanently on the stack.
func createSimpleVars(apDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) { func createSimpleVars(fnsym *obj.LSym, apDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) {
var vars []*dwarf.Var var vars []*dwarf.Var
var decls []*Node var decls []*Node
selected := make(map[*Node]bool) selected := make(map[*Node]bool)
@ -490,13 +491,13 @@ func createSimpleVars(apDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) {
} }
decls = append(decls, n) decls = append(decls, n)
vars = append(vars, createSimpleVar(n)) vars = append(vars, createSimpleVar(fnsym, n))
selected[n] = true selected[n] = true
} }
return decls, vars, selected return decls, vars, selected
} }
func createSimpleVar(n *Node) *dwarf.Var { func createSimpleVar(fnsym *obj.LSym, n *Node) *dwarf.Var {
var abbrev int var abbrev int
offs := n.Xoffset offs := n.Xoffset
@ -506,7 +507,7 @@ func createSimpleVar(n *Node) *dwarf.Var {
if Ctxt.FixedFrameSize() == 0 { if Ctxt.FixedFrameSize() == 0 {
offs -= int64(Widthptr) offs -= int64(Widthptr)
} }
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" { if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled // There is a word space for FP on ARM64 even if the frame pointer is disabled
offs -= int64(Widthptr) offs -= int64(Widthptr)
} }
@ -519,6 +520,7 @@ func createSimpleVar(n *Node) *dwarf.Var {
} }
typename := dwarf.InfoPrefix + typesymname(n.Type) typename := dwarf.InfoPrefix + typesymname(n.Type)
delete(fnsym.Func.Autot, ngotype(n).Linksym())
inlIndex := 0 inlIndex := 0
if genDwarfInline > 1 { if genDwarfInline > 1 {
if n.Name.InlFormal() || n.Name.InlLocal() { if n.Name.InlFormal() || n.Name.InlLocal() {
@ -546,7 +548,7 @@ func createSimpleVar(n *Node) *dwarf.Var {
// createComplexVars creates recomposed DWARF vars with location lists, // createComplexVars creates recomposed DWARF vars with location lists,
// suitable for describing optimized code. // suitable for describing optimized code.
func createComplexVars(fn *Func) ([]*Node, []*dwarf.Var, map[*Node]bool) { func createComplexVars(fnsym *obj.LSym, fn *Func) ([]*Node, []*dwarf.Var, map[*Node]bool) {
debugInfo := fn.DebugInfo debugInfo := fn.DebugInfo
// Produce a DWARF variable entry for each user variable. // Produce a DWARF variable entry for each user variable.
@ -561,7 +563,7 @@ func createComplexVars(fn *Func) ([]*Node, []*dwarf.Var, map[*Node]bool) {
ssaVars[debugInfo.Slots[slot].N.(*Node)] = true ssaVars[debugInfo.Slots[slot].N.(*Node)] = true
} }
if dvar := createComplexVar(fn, ssa.VarID(varID)); dvar != nil { if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil {
decls = append(decls, n) decls = append(decls, n)
vars = append(vars, dvar) vars = append(vars, dvar)
} }
@ -578,9 +580,9 @@ func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dw
var decls []*Node var decls []*Node
var selected map[*Node]bool var selected map[*Node]bool
if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil { if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil {
decls, vars, selected = createComplexVars(fn) decls, vars, selected = createComplexVars(fnsym, fn)
} else { } else {
decls, vars, selected = createSimpleVars(apDecls) decls, vars, selected = createSimpleVars(fnsym, apDecls)
} }
dcl := apDecls dcl := apDecls
@ -616,7 +618,7 @@ func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dw
// Args not of SSA-able type are treated here; they // Args not of SSA-able type are treated here; they
// are homed on the stack in a single place for the // are homed on the stack in a single place for the
// entire call. // entire call.
vars = append(vars, createSimpleVar(n)) vars = append(vars, createSimpleVar(fnsym, n))
decls = append(decls, n) decls = append(decls, n)
continue continue
} }
@ -701,7 +703,7 @@ func stackOffset(slot ssa.LocalSlot) int32 {
if Ctxt.FixedFrameSize() == 0 { if Ctxt.FixedFrameSize() == 0 {
base -= int64(Widthptr) base -= int64(Widthptr)
} }
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" { if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled // There is a word space for FP on ARM64 even if the frame pointer is disabled
base -= int64(Widthptr) base -= int64(Widthptr)
} }
@ -712,7 +714,7 @@ func stackOffset(slot ssa.LocalSlot) int32 {
} }
// createComplexVar builds a single DWARF variable entry and location list. // createComplexVar builds a single DWARF variable entry and location list.
func createComplexVar(fn *Func, varID ssa.VarID) *dwarf.Var { func createComplexVar(fnsym *obj.LSym, fn *Func, varID ssa.VarID) *dwarf.Var {
debug := fn.DebugInfo debug := fn.DebugInfo
n := debug.Vars[varID].(*Node) n := debug.Vars[varID].(*Node)
@ -727,6 +729,7 @@ func createComplexVar(fn *Func, varID ssa.VarID) *dwarf.Var {
} }
gotype := ngotype(n).Linksym() gotype := ngotype(n).Linksym()
delete(fnsym.Func.Autot, gotype)
typename := dwarf.InfoPrefix + gotype.Name[len("type."):] typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
inlIndex := 0 inlIndex := 0
if genDwarfInline > 1 { if genDwarfInline > 1 {

View file

@ -20,7 +20,7 @@ func typeWithoutPointers() *types.Type {
func typeWithPointers() *types.Type { func typeWithPointers() *types.Type {
t := types.New(TSTRUCT) t := types.New(TSTRUCT)
f := &types.Field{Type: types.New(TPTR)} f := &types.Field{Type: types.NewPtr(types.New(TINT))}
t.SetFields([]*types.Field{f}) t.SetFields([]*types.Field{f})
return t return t
} }
@ -181,14 +181,6 @@ func TestStackvarSort(t *testing.T) {
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO), nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO), nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
} }
// haspointers updates Type.Haspointers as a side effect, so
// exercise this function on all inputs so that reflect.DeepEqual
// doesn't produce false positives.
for i := range want {
types.Haspointers(want[i].Type)
types.Haspointers(inp[i].Type)
}
sort.Sort(byStackVar(inp)) sort.Sort(byStackVar(inp))
if !reflect.DeepEqual(want, inp) { if !reflect.DeepEqual(want, inp) {
t.Error("sort failed") t.Error("sort failed")

View file

@ -140,24 +140,14 @@ type Liveness struct {
regMaps []liveRegMask regMaps []liveRegMask
cache progeffectscache cache progeffectscache
// These are only populated if open-coded defers are being used.
// List of vars/stack slots storing defer args
openDeferVars []openDeferVarInfo
// Map from defer arg OpVarDef to the block where the OpVarDef occurs.
openDeferVardefToBlockMap map[*Node]*ssa.Block
// Map of blocks that cannot reach a return or exit (panic)
nonReturnBlocks map[*ssa.Block]bool
}
type openDeferVarInfo struct {
n *Node // Var/stack slot storing a defer arg
varsIndex int // Index of variable in lv.vars
} }
// LivenessMap maps from *ssa.Value to LivenessIndex. // LivenessMap maps from *ssa.Value to LivenessIndex.
type LivenessMap struct { type LivenessMap struct {
vals map[ssa.ID]LivenessIndex vals map[ssa.ID]LivenessIndex
// The set of live, pointer-containing variables at the deferreturn
// call (only set when open-coded defers are used).
deferreturn LivenessIndex
} }
func (m *LivenessMap) reset() { func (m *LivenessMap) reset() {
@ -168,6 +158,7 @@ func (m *LivenessMap) reset() {
delete(m.vals, k) delete(m.vals, k)
} }
} }
m.deferreturn = LivenessInvalid
} }
func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) { func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
@ -268,7 +259,7 @@ func (v *varRegVec) AndNot(v1, v2 varRegVec) {
// nor do we care about empty structs (handled by the pointer check), // nor do we care about empty structs (handled by the pointer check),
// nor do we care about the fake PAUTOHEAP variables. // nor do we care about the fake PAUTOHEAP variables.
func livenessShouldTrack(n *Node) bool { func livenessShouldTrack(n *Node) bool {
return n.Op == ONAME && (n.Class() == PAUTO || n.Class() == PPARAM || n.Class() == PPARAMOUT) && types.Haspointers(n.Type) return n.Op == ONAME && (n.Class() == PAUTO || n.Class() == PPARAM || n.Class() == PPARAMOUT) && n.Type.HasPointers()
} }
// getvariables returns the list of on-stack variables that we need to track // getvariables returns the list of on-stack variables that we need to track
@ -445,7 +436,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
case ssa.LocalSlot: case ssa.LocalSlot:
return mask return mask
case *ssa.Register: case *ssa.Register:
if ptrOnly && !v.Type.HasHeapPointer() { if ptrOnly && !v.Type.HasPointers() {
return mask return mask
} }
regs[0] = loc regs[0] = loc
@ -460,7 +451,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
if loc1 == nil { if loc1 == nil {
continue continue
} }
if ptrOnly && !v.Type.FieldType(i).HasHeapPointer() { if ptrOnly && !v.Type.FieldType(i).HasPointers() {
continue continue
} }
regs[nreg] = loc1.(*ssa.Register) regs[nreg] = loc1.(*ssa.Register)
@ -542,7 +533,7 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
if cap(lc.be) >= f.NumBlocks() { if cap(lc.be) >= f.NumBlocks() {
lv.be = lc.be[:f.NumBlocks()] lv.be = lc.be[:f.NumBlocks()]
} }
lv.livenessMap = LivenessMap{lc.livenessMap.vals} lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessInvalid}
lc.livenessMap.vals = nil lc.livenessMap.vals = nil
} }
if lv.be == nil { if lv.be == nil {
@ -577,13 +568,13 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
if t.Align > 0 && off&int64(t.Align-1) != 0 { if t.Align > 0 && off&int64(t.Align-1) != 0 {
Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off) Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
} }
if !t.HasPointers() {
// Note: this case ensures that pointers to go:notinheap types
// are not considered pointers by garbage collection and stack copying.
return
}
switch t.Etype { switch t.Etype {
case TINT8, TUINT8, TINT16, TUINT16,
TINT32, TUINT32, TINT64, TUINT64,
TINT, TUINT, TUINTPTR, TBOOL,
TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP: case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
if off&int64(Widthptr-1) != 0 { if off&int64(Widthptr-1) != 0 {
Fatalf("onebitwalktype1: invalid alignment, %v", t) Fatalf("onebitwalktype1: invalid alignment, %v", t)
@ -870,7 +861,7 @@ func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
// typedmemclr and typedmemmove are write barriers and // typedmemclr and typedmemmove are write barriers and
// deeply non-preemptible. They are unsafe points and // deeply non-preemptible. They are unsafe points and
// hence should not have liveness maps. // hence should not have liveness maps.
if sym, _ := v.Aux.(*obj.LSym); sym == typedmemclr || sym == typedmemmove { if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
return false return false
} }
return true return true
@ -893,58 +884,12 @@ func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
func (lv *Liveness) prologue() { func (lv *Liveness) prologue() {
lv.initcache() lv.initcache()
if lv.fn.Func.HasDefer() && !lv.fn.Func.OpenCodedDeferDisallowed() {
lv.openDeferVardefToBlockMap = make(map[*Node]*ssa.Block)
for i, n := range lv.vars {
if n.Name.OpenDeferSlot() {
lv.openDeferVars = append(lv.openDeferVars, openDeferVarInfo{n: n, varsIndex: i})
}
}
// Find any blocks that cannot reach a return or a BlockExit
// (panic) -- these must be because of an infinite loop.
reachesRet := make(map[ssa.ID]bool)
blockList := make([]*ssa.Block, 0, 256)
for _, b := range lv.f.Blocks {
if b.Kind == ssa.BlockRet || b.Kind == ssa.BlockRetJmp || b.Kind == ssa.BlockExit {
blockList = append(blockList, b)
}
}
for len(blockList) > 0 {
b := blockList[0]
blockList = blockList[1:]
if reachesRet[b.ID] {
continue
}
reachesRet[b.ID] = true
for _, e := range b.Preds {
blockList = append(blockList, e.Block())
}
}
lv.nonReturnBlocks = make(map[*ssa.Block]bool)
for _, b := range lv.f.Blocks {
if !reachesRet[b.ID] {
lv.nonReturnBlocks[b] = true
//fmt.Println("No reach ret", lv.f.Name, b.ID, b.Kind)
}
}
}
for _, b := range lv.f.Blocks { for _, b := range lv.f.Blocks {
be := lv.blockEffects(b) be := lv.blockEffects(b)
// Walk the block instructions backward and update the block // Walk the block instructions backward and update the block
// effects with the each prog effects. // effects with the each prog effects.
for j := len(b.Values) - 1; j >= 0; j-- { for j := len(b.Values) - 1; j >= 0; j-- {
if b.Values[j].Op == ssa.OpVarDef {
n := b.Values[j].Aux.(*Node)
if n.Name.OpenDeferSlot() {
lv.openDeferVardefToBlockMap[n] = b
}
}
pos, e := lv.valueEffects(b.Values[j]) pos, e := lv.valueEffects(b.Values[j])
regUevar, regKill := lv.regEffects(b.Values[j]) regUevar, regKill := lv.regEffects(b.Values[j])
if e&varkill != 0 { if e&varkill != 0 {
@ -961,20 +906,6 @@ func (lv *Liveness) prologue() {
} }
} }
// markDeferVarsLive marks each variable storing an open-coded defer arg as
// specially live in block b if the variable definition dominates block b.
func (lv *Liveness) markDeferVarsLive(b *ssa.Block, newliveout *varRegVec) {
// Only force computation of dominators if we have a block where we need
// to specially mark defer args live.
sdom := lv.f.Sdom()
for _, info := range lv.openDeferVars {
defB := lv.openDeferVardefToBlockMap[info.n]
if sdom.IsAncestorEq(defB, b) {
newliveout.vars.Set(int32(info.varsIndex))
}
}
}
// Solve the liveness dataflow equations. // Solve the liveness dataflow equations.
func (lv *Liveness) solve() { func (lv *Liveness) solve() {
// These temporary bitvectors exist to avoid successive allocations and // These temporary bitvectors exist to avoid successive allocations and
@ -1018,23 +949,6 @@ func (lv *Liveness) solve() {
} }
} }
if lv.fn.Func.HasDefer() && !lv.fn.Func.OpenCodedDeferDisallowed() &&
(b.Kind == ssa.BlockExit || lv.nonReturnBlocks[b]) {
// Open-coded defer args slots must be live
// everywhere in a function, since a panic can
// occur (almost) anywhere. Force all appropriate
// defer arg slots to be live in BlockExit (panic)
// blocks and in blocks that do not reach a return
// (because of infinite loop).
//
// We are assuming that the defer exit code at
// BlockReturn/BlockReturnJmp accesses all of the
// defer args (with pointers), and so keeps them
// live. This analysis may have to be adjusted if
// that changes (because of optimizations).
lv.markDeferVarsLive(b, &newliveout)
}
if !be.liveout.Eq(newliveout) { if !be.liveout.Eq(newliveout) {
change = true change = true
be.liveout.Copy(newliveout) be.liveout.Copy(newliveout)
@ -1087,6 +1001,17 @@ func (lv *Liveness) epilogue() {
n.Name.SetNeedzero(true) n.Name.SetNeedzero(true)
livedefer.Set(int32(i)) livedefer.Set(int32(i))
} }
if n.Name.OpenDeferSlot() {
// Open-coded defer args slots must be live
// everywhere in a function, since a panic can
// occur (almost) anywhere. Because it is live
// everywhere, it must be zeroed on entry.
livedefer.Set(int32(i))
// It was already marked as Needzero when created.
if !n.Name.Needzero() {
Fatalf("all pointer-containing defer arg slots should have Needzero set")
}
}
} }
} }
@ -1188,6 +1113,17 @@ func (lv *Liveness) epilogue() {
lv.compact(b) lv.compact(b)
} }
// If we have an open-coded deferreturn call, make a liveness map for it.
if lv.fn.Func.OpenCodedDeferDisallowed() {
lv.livenessMap.deferreturn = LivenessInvalid
} else {
lv.livenessMap.deferreturn = LivenessIndex{
stackMapIndex: lv.stackMapSet.add(livedefer),
regMapIndex: 0, // entry regMap, containing no live registers
isUnsafePoint: false,
}
}
// Done compacting. Throw out the stack map set. // Done compacting. Throw out the stack map set.
lv.stackMaps = lv.stackMapSet.extractUniqe() lv.stackMaps = lv.stackMapSet.extractUniqe()
lv.stackMapSet = bvecSet{} lv.stackMapSet = bvecSet{}
@ -1295,8 +1231,8 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
s := "live at " s := "live at "
if v == nil { if v == nil {
s += fmt.Sprintf("entry to %s:", lv.fn.funcname()) s += fmt.Sprintf("entry to %s:", lv.fn.funcname())
} else if sym, ok := v.Aux.(*obj.LSym); ok { } else if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
fn := sym.Name fn := sym.Fn.Name
if pos := strings.Index(fn, "."); pos >= 0 { if pos := strings.Index(fn, "."); pos >= 0 {
fn = fn[pos+1:] fn = fn[pos+1:]
} }
@ -1563,6 +1499,7 @@ func (lv *Liveness) emit() (argsSym, liveSym, regsSym *obj.LSym) {
makeSym := func(tmpSym *obj.LSym) *obj.LSym { makeSym := func(tmpSym *obj.LSym) *obj.LSym {
return Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) { return Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) {
lsym.P = tmpSym.P lsym.P = tmpSym.P
lsym.Set(obj.AttrContentAddressable, true)
}) })
} }
if !go115ReduceLiveness { if !go115ReduceLiveness {

View file

@ -42,7 +42,7 @@ var omit_pkgs = []string{
"internal/cpu", "internal/cpu",
} }
// Only insert racefuncenterfp/racefuncexit into the following packages. // Don't insert racefuncenterfp/racefuncexit into the following packages.
// Memory accesses in the packages are either uninteresting or will cause false positives. // Memory accesses in the packages are either uninteresting or will cause false positives.
var norace_inst_pkgs = []string{"sync", "sync/atomic"} var norace_inst_pkgs = []string{"sync", "sync/atomic"}

View file

@ -334,7 +334,7 @@ func walkrange(n *Node) *Node {
hv1 := temp(t.Elem()) hv1 := temp(t.Elem())
hv1.SetTypecheck(1) hv1.SetTypecheck(1)
if types.Haspointers(t.Elem()) { if t.Elem().HasPointers() {
init = append(init, nod(OAS, hv1, nil)) init = append(init, nod(OAS, hv1, nil))
} }
hb := temp(types.Types[TBOOL]) hb := temp(types.Types[TBOOL])
@ -586,7 +586,7 @@ func arrayClear(n, v1, v2, a *Node) bool {
n.Nbody.Append(nod(OAS, hn, tmp)) n.Nbody.Append(nod(OAS, hn, tmp))
var fn *Node var fn *Node
if a.Type.Elem().HasHeapPointer() { if a.Type.Elem().HasPointers() {
// memclrHasPointers(hp, hn) // memclrHasPointers(hp, hn)
Curfn.Func.setWBPos(stmt.Pos) Curfn.Func.setWBPos(stmt.Pos)
fn = mkcall("memclrHasPointers", nil, nil, hp, hn) fn = mkcall("memclrHasPointers", nil, nil, hp, hn)

View file

@ -119,7 +119,7 @@ func bmap(t *types.Type) *types.Type {
// the type of the overflow field to uintptr in this case. // the type of the overflow field to uintptr in this case.
// See comment on hmap.overflow in runtime/map.go. // See comment on hmap.overflow in runtime/map.go.
otyp := types.NewPtr(bucket) otyp := types.NewPtr(bucket)
if !types.Haspointers(elemtype) && !types.Haspointers(keytype) { if !elemtype.HasPointers() && !keytype.HasPointers() {
otyp = types.Types[TUINTPTR] otyp = types.Types[TUINTPTR]
} }
overflow := makefield("overflow", otyp) overflow := makefield("overflow", otyp)
@ -754,7 +754,7 @@ var kinds = []int{
// typeptrdata returns the length in bytes of the prefix of t // typeptrdata returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data. // containing pointer data. Anything after this offset is scalar data.
func typeptrdata(t *types.Type) int64 { func typeptrdata(t *types.Type) int64 {
if !types.Haspointers(t) { if !t.HasPointers() {
return 0 return 0
} }
@ -788,7 +788,7 @@ func typeptrdata(t *types.Type) int64 {
// Find the last field that has pointers. // Find the last field that has pointers.
var lastPtrField *types.Field var lastPtrField *types.Field
for _, t1 := range t.Fields().Slice() { for _, t1 := range t.Fields().Slice() {
if types.Haspointers(t1.Type) { if t1.Type.HasPointers() {
lastPtrField = t1 lastPtrField = t1
} }
} }
@ -1168,6 +1168,15 @@ func dtypesym(t *types.Type) *obj.LSym {
if myimportpath != "runtime" || (tbase != types.Types[tbase.Etype] && tbase != types.Bytetype && tbase != types.Runetype && tbase != types.Errortype) { // int, float, etc if myimportpath != "runtime" || (tbase != types.Types[tbase.Etype] && tbase != types.Bytetype && tbase != types.Runetype && tbase != types.Errortype) { // int, float, etc
// named types from other files are defined only by those files // named types from other files are defined only by those files
if tbase.Sym != nil && tbase.Sym.Pkg != localpkg { if tbase.Sym != nil && tbase.Sym.Pkg != localpkg {
if i, ok := typeSymIdx[tbase]; ok {
lsym.Pkg = tbase.Sym.Pkg.Prefix
if t != tbase {
lsym.SymIdx = int32(i[1])
} else {
lsym.SymIdx = int32(i[0])
}
lsym.Set(obj.AttrIndexed, true)
}
return lsym return lsym
} }
// TODO(mdempsky): Investigate whether this can happen. // TODO(mdempsky): Investigate whether this can happen.
@ -1577,9 +1586,7 @@ func dumptabs() {
} }
// Nothing writes static itabs, so they are read only. // Nothing writes static itabs, so they are read only.
ggloblsym(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA)) ggloblsym(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
ilink := itablinkpkg.Lookup(i.t.ShortString() + "," + i.itype.ShortString()).Linksym() i.lsym.Set(obj.AttrContentAddressable, true)
dsymptr(ilink, 0, i.lsym, 0)
ggloblsym(ilink, int32(Widthptr), int16(obj.DUPOK|obj.RODATA))
} }
// process ptabs // process ptabs
@ -1742,6 +1749,7 @@ func dgcptrmask(t *types.Type) *obj.LSym {
duint8(lsym, i, x) duint8(lsym, i, x)
} }
ggloblsym(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL) ggloblsym(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
lsym.Set(obj.AttrContentAddressable, true)
} }
return lsym return lsym
} }
@ -1753,7 +1761,7 @@ func fillptrmask(t *types.Type, ptrmask []byte) {
for i := range ptrmask { for i := range ptrmask {
ptrmask[i] = 0 ptrmask[i] = 0
} }
if !types.Haspointers(t) { if !t.HasPointers() {
return return
} }
@ -1822,7 +1830,7 @@ func (p *GCProg) end() {
func (p *GCProg) emit(t *types.Type, offset int64) { func (p *GCProg) emit(t *types.Type, offset int64) {
dowidth(t) dowidth(t)
if !types.Haspointers(t) { if !t.HasPointers() {
return return
} }
if t.Width == int64(Widthptr) { if t.Width == int64(Widthptr) {

View file

@ -106,18 +106,16 @@ func walkselect(sel *Node) {
} }
func walkselectcases(cases *Nodes) []*Node { func walkselectcases(cases *Nodes) []*Node {
n := cases.Len() ncas := cases.Len()
sellineno := lineno sellineno := lineno
// optimization: zero-case select // optimization: zero-case select
if n == 0 { if ncas == 0 {
return []*Node{mkcall("block", nil, nil)} return []*Node{mkcall("block", nil, nil)}
} }
// optimization: one-case select: single op. // optimization: one-case select: single op.
// TODO(rsc): Reenable optimization once order.go can handle it. if ncas == 1 {
// golang.org/issue/7672.
if n == 1 {
cas := cases.First() cas := cases.First()
setlineno(cas) setlineno(cas)
l := cas.Ninit.Slice() l := cas.Ninit.Slice()
@ -125,17 +123,14 @@ func walkselectcases(cases *Nodes) []*Node {
n := cas.Left n := cas.Left
l = append(l, n.Ninit.Slice()...) l = append(l, n.Ninit.Slice()...)
n.Ninit.Set(nil) n.Ninit.Set(nil)
var ch *Node
switch n.Op { switch n.Op {
default: default:
Fatalf("select %v", n.Op) Fatalf("select %v", n.Op)
// ok already
case OSEND: case OSEND:
ch = n.Left // already ok
case OSELRECV, OSELRECV2: case OSELRECV, OSELRECV2:
ch = n.Right.Left
if n.Op == OSELRECV || n.List.Len() == 0 { if n.Op == OSELRECV || n.List.Len() == 0 {
if n.Left == nil { if n.Left == nil {
n = n.Right n = n.Right
@ -159,16 +154,7 @@ func walkselectcases(cases *Nodes) []*Node {
n = typecheck(n, ctxStmt) n = typecheck(n, ctxStmt)
} }
// if ch == nil { block() }; n; l = append(l, n)
a := nod(OIF, nil, nil)
a.Left = nod(OEQ, ch, nodnil())
var ln Nodes
ln.Set(l)
a.Nbody.Set1(mkcall("block", nil, &ln))
l = ln.Slice()
a = typecheck(a, ctxStmt)
l = append(l, a, n)
} }
l = append(l, cas.Nbody.Slice()...) l = append(l, cas.Nbody.Slice()...)
@ -178,10 +164,12 @@ func walkselectcases(cases *Nodes) []*Node {
// convert case value arguments to addresses. // convert case value arguments to addresses.
// this rewrite is used by both the general code and the next optimization. // this rewrite is used by both the general code and the next optimization.
var dflt *Node
for _, cas := range cases.Slice() { for _, cas := range cases.Slice() {
setlineno(cas) setlineno(cas)
n := cas.Left n := cas.Left
if n == nil { if n == nil {
dflt = cas
continue continue
} }
switch n.Op { switch n.Op {
@ -202,15 +190,10 @@ func walkselectcases(cases *Nodes) []*Node {
} }
// optimization: two-case select but one is default: single non-blocking op. // optimization: two-case select but one is default: single non-blocking op.
if n == 2 && (cases.First().Left == nil || cases.Second().Left == nil) { if ncas == 2 && dflt != nil {
var cas *Node cas := cases.First()
var dflt *Node if cas == dflt {
if cases.First().Left == nil {
cas = cases.Second() cas = cases.Second()
dflt = cases.First()
} else {
dflt = cases.Second()
cas = cases.First()
} }
n := cas.Left n := cas.Left
@ -228,8 +211,6 @@ func walkselectcases(cases *Nodes) []*Node {
case OSELRECV: case OSELRECV:
// if selectnbrecv(&v, c) { body } else { default body } // if selectnbrecv(&v, c) { body } else { default body }
r = nod(OIF, nil, nil)
r.Ninit.Set(cas.Ninit.Slice())
ch := n.Right.Left ch := n.Right.Left
elem := n.Left elem := n.Left
if elem == nil { if elem == nil {
@ -239,8 +220,6 @@ func walkselectcases(cases *Nodes) []*Node {
case OSELRECV2: case OSELRECV2:
// if selectnbrecv2(&v, &received, c) { body } else { default body } // if selectnbrecv2(&v, &received, c) { body } else { default body }
r = nod(OIF, nil, nil)
r.Ninit.Set(cas.Ninit.Slice())
ch := n.Right.Left ch := n.Right.Left
elem := n.Left elem := n.Left
if elem == nil { if elem == nil {
@ -257,66 +236,71 @@ func walkselectcases(cases *Nodes) []*Node {
return []*Node{r, nod(OBREAK, nil, nil)} return []*Node{r, nod(OBREAK, nil, nil)}
} }
if dflt != nil {
ncas--
}
casorder := make([]*Node, ncas)
nsends, nrecvs := 0, 0
var init []*Node var init []*Node
// generate sel-struct // generate sel-struct
lineno = sellineno lineno = sellineno
selv := temp(types.NewArray(scasetype(), int64(n))) selv := temp(types.NewArray(scasetype(), int64(ncas)))
r := nod(OAS, selv, nil) r := nod(OAS, selv, nil)
r = typecheck(r, ctxStmt) r = typecheck(r, ctxStmt)
init = append(init, r) init = append(init, r)
order := temp(types.NewArray(types.Types[TUINT16], 2*int64(n))) // No initialization for order; runtime.selectgo is responsible for that.
r = nod(OAS, order, nil) order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas)))
r = typecheck(r, ctxStmt)
init = append(init, r) var pc0, pcs *Node
if flag_race {
pcs = temp(types.NewArray(types.Types[TUINTPTR], int64(ncas)))
pc0 = typecheck(nod(OADDR, nod(OINDEX, pcs, nodintconst(0)), nil), ctxExpr)
} else {
pc0 = nodnil()
}
// register cases // register cases
for i, cas := range cases.Slice() { for _, cas := range cases.Slice() {
setlineno(cas) setlineno(cas)
init = append(init, cas.Ninit.Slice()...) init = append(init, cas.Ninit.Slice()...)
cas.Ninit.Set(nil) cas.Ninit.Set(nil)
// Keep in sync with runtime/select.go. n := cas.Left
const ( if n == nil { // default:
caseNil = iota continue
caseRecv
caseSend
caseDefault
)
var c, elem *Node
var kind int64 = caseDefault
if n := cas.Left; n != nil {
init = append(init, n.Ninit.Slice()...)
switch n.Op {
default:
Fatalf("select %v", n.Op)
case OSEND:
kind = caseSend
c = n.Left
elem = n.Right
case OSELRECV, OSELRECV2:
kind = caseRecv
c = n.Right.Left
elem = n.Left
}
} }
var i int
var c, elem *Node
switch n.Op {
default:
Fatalf("select %v", n.Op)
case OSEND:
i = nsends
nsends++
c = n.Left
elem = n.Right
case OSELRECV, OSELRECV2:
nrecvs++
i = ncas - nrecvs
c = n.Right.Left
elem = n.Left
}
casorder[i] = cas
setField := func(f string, val *Node) { setField := func(f string, val *Node) {
r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val) r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
r = typecheck(r, ctxStmt) r = typecheck(r, ctxStmt)
init = append(init, r) init = append(init, r)
} }
setField("kind", nodintconst(kind)) c = convnop(c, types.Types[TUNSAFEPTR])
if c != nil { setField("c", c)
c = convnop(c, types.Types[TUNSAFEPTR])
setField("c", c)
}
if elem != nil { if elem != nil {
elem = convnop(elem, types.Types[TUNSAFEPTR]) elem = convnop(elem, types.Types[TUNSAFEPTR])
setField("elem", elem) setField("elem", elem)
@ -324,11 +308,14 @@ func walkselectcases(cases *Nodes) []*Node {
// TODO(mdempsky): There should be a cleaner way to // TODO(mdempsky): There should be a cleaner way to
// handle this. // handle this.
if instrumenting { if flag_race {
r = mkcall("selectsetpc", nil, nil, bytePtrToIndex(selv, int64(i))) r = mkcall("selectsetpc", nil, nil, nod(OADDR, nod(OINDEX, pcs, nodintconst(int64(i))), nil))
init = append(init, r) init = append(init, r)
} }
} }
if nsends+nrecvs != ncas {
Fatalf("walkselectcases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
}
// run the select // run the select
lineno = sellineno lineno = sellineno
@ -337,23 +324,23 @@ func walkselectcases(cases *Nodes) []*Node {
r = nod(OAS2, nil, nil) r = nod(OAS2, nil, nil)
r.List.Set2(chosen, recvOK) r.List.Set2(chosen, recvOK)
fn := syslook("selectgo") fn := syslook("selectgo")
r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), nodintconst(int64(n)))) r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, nodintconst(int64(nsends)), nodintconst(int64(nrecvs)), nodbool(dflt == nil)))
r = typecheck(r, ctxStmt) r = typecheck(r, ctxStmt)
init = append(init, r) init = append(init, r)
// selv and order are no longer alive after selectgo. // selv and order are no longer alive after selectgo.
init = append(init, nod(OVARKILL, selv, nil)) init = append(init, nod(OVARKILL, selv, nil))
init = append(init, nod(OVARKILL, order, nil)) init = append(init, nod(OVARKILL, order, nil))
if flag_race {
init = append(init, nod(OVARKILL, pcs, nil))
}
// dispatch cases // dispatch cases
for i, cas := range cases.Slice() { dispatch := func(cond, cas *Node) {
setlineno(cas)
cond := nod(OEQ, chosen, nodintconst(int64(i)))
cond = typecheck(cond, ctxExpr) cond = typecheck(cond, ctxExpr)
cond = defaultlit(cond, nil) cond = defaultlit(cond, nil)
r = nod(OIF, cond, nil) r := nod(OIF, cond, nil)
if n := cas.Left; n != nil && n.Op == OSELRECV2 { if n := cas.Left; n != nil && n.Op == OSELRECV2 {
x := nod(OAS, n.List.First(), recvOK) x := nod(OAS, n.List.First(), recvOK)
@ -366,6 +353,15 @@ func walkselectcases(cases *Nodes) []*Node {
init = append(init, r) init = append(init, r)
} }
if dflt != nil {
setlineno(dflt)
dispatch(nod(OLT, chosen, nodintconst(0)), dflt)
}
for i, cas := range casorder {
setlineno(cas)
dispatch(nod(OEQ, chosen, nodintconst(int64(i))), cas)
}
return init return init
} }
@ -384,9 +380,6 @@ func scasetype() *types.Type {
scase = tostruct([]*Node{ scase = tostruct([]*Node{
namedfield("c", types.Types[TUNSAFEPTR]), namedfield("c", types.Types[TUNSAFEPTR]),
namedfield("elem", types.Types[TUNSAFEPTR]), namedfield("elem", types.Types[TUNSAFEPTR]),
namedfield("kind", types.Types[TUINT16]),
namedfield("pc", types.Types[TUINTPTR]),
namedfield("releasetime", types.Types[TINT64]),
}) })
scase.SetNoalg(true) scase.SetNoalg(true)
} }

View file

@ -6,6 +6,7 @@ package gc
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj"
"fmt" "fmt"
) )
@ -277,6 +278,8 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
return Isconst(val, CTNIL) return Isconst(val, CTNIL)
} }
markTypeUsedInInterface(val.Type)
var itab *Node var itab *Node
if l.Type.IsEmptyInterface() { if l.Type.IsEmptyInterface() {
itab = typename(val.Type) itab = typename(val.Type)
@ -353,14 +356,22 @@ func (c initContext) String() string {
var statuniqgen int // name generator for static temps var statuniqgen int // name generator for static temps
// staticname returns a name backed by a static data symbol. // staticname returns a name backed by a (writable) static data symbol.
// Callers should call n.MarkReadonly on the // Use readonlystaticname for read-only node.
// returned node for readonly nodes.
func staticname(t *types.Type) *Node { func staticname(t *types.Type) *Node {
// Don't use lookupN; it interns the resulting string, but these are all unique. // Don't use lookupN; it interns the resulting string, but these are all unique.
n := newname(lookup(fmt.Sprintf(".stmp_%d", statuniqgen))) n := newname(lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)))
statuniqgen++ statuniqgen++
addvar(n, t, PEXTERN) addvar(n, t, PEXTERN)
n.Sym.Linksym().Set(obj.AttrLocal, true)
return n
}
// readonlystaticname returns a name backed by a (writable) static data symbol.
func readonlystaticname(t *types.Type) *Node {
n := staticname(t)
n.MarkReadonly()
n.Sym.Linksym().Set(obj.AttrContentAddressable, true)
return n return n
} }
@ -495,6 +506,7 @@ const (
// fixedlit handles struct, array, and slice literals. // fixedlit handles struct, array, and slice literals.
// TODO: expand documentation. // TODO: expand documentation.
func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) { func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
isBlank := var_ == nblank
var splitnode func(*Node) (a *Node, value *Node) var splitnode func(*Node) (a *Node, value *Node)
switch n.Op { switch n.Op {
case OARRAYLIT, OSLICELIT: case OARRAYLIT, OSLICELIT:
@ -509,6 +521,9 @@ func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes)
} }
a := nod(OINDEX, var_, nodintconst(k)) a := nod(OINDEX, var_, nodintconst(k))
k++ k++
if isBlank {
a = nblank
}
return a, r return a, r
} }
case OSTRUCTLIT: case OSTRUCTLIT:
@ -516,7 +531,7 @@ func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes)
if r.Op != OSTRUCTKEY { if r.Op != OSTRUCTKEY {
Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r) Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r)
} }
if r.Sym.IsBlank() { if r.Sym.IsBlank() || isBlank {
return nblank, r.Left return nblank, r.Left
} }
setlineno(r) setlineno(r)
@ -624,9 +639,10 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
mode := getdyn(n, true) mode := getdyn(n, true)
if mode&initConst != 0 && !isSmallSliceLit(n) { if mode&initConst != 0 && !isSmallSliceLit(n) {
vstat = staticname(t)
if ctxt == inInitFunction { if ctxt == inInitFunction {
vstat.MarkReadonly() vstat = readonlystaticname(t)
} else {
vstat = staticname(t)
} }
fixedlit(ctxt, initKindStatic, n, vstat, init) fixedlit(ctxt, initKindStatic, n, vstat, init)
} }
@ -770,10 +786,8 @@ func maplit(n *Node, m *Node, init *Nodes) {
dowidth(te) dowidth(te)
// make and initialize static arrays // make and initialize static arrays
vstatk := staticname(tk) vstatk := readonlystaticname(tk)
vstatk.MarkReadonly() vstate := readonlystaticname(te)
vstate := staticname(te)
vstate.MarkReadonly()
datak := nod(OARRAYLIT, nil, nil) datak := nod(OARRAYLIT, nil, nil)
datae := nod(OARRAYLIT, nil, nil) datae := nod(OARRAYLIT, nil, nil)
@ -894,8 +908,7 @@ func anylit(n *Node, var_ *Node, init *Nodes) {
if var_.isSimpleName() && n.List.Len() > 4 { if var_.isSimpleName() && n.List.Len() > 4 {
// lay out static data // lay out static data
vstat := staticname(t) vstat := readonlystaticname(t)
vstat.MarkReadonly()
ctxt := inInitFunction ctxt := inInitFunction
if n.Op == OARRAYLIT { if n.Op == OARRAYLIT {

View file

@ -10,6 +10,7 @@ import (
"html" "html"
"os" "os"
"sort" "sort"
"strings"
"bufio" "bufio"
"bytes" "bytes"
@ -295,7 +296,10 @@ func (s *state) emitOpenDeferInfo() {
// worker indicates which of the backend workers is doing the processing. // worker indicates which of the backend workers is doing the processing.
func buildssa(fn *Node, worker int) *ssa.Func { func buildssa(fn *Node, worker int) *ssa.Func {
name := fn.funcname() name := fn.funcname()
printssa := name == ssaDump printssa := false
if ssaDump != "" { // match either a simple name e.g. "(*Reader).Reset", or a package.name e.g. "compress/gzip.(*Reader).Reset"
printssa = name == ssaDump || myimportpath+"."+name == ssaDump
}
var astBuf *bytes.Buffer var astBuf *bytes.Buffer
if printssa { if printssa {
astBuf = &bytes.Buffer{} astBuf = &bytes.Buffer{}
@ -329,8 +333,8 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.f.Config = ssaConfig s.f.Config = ssaConfig
s.f.Cache = &ssaCaches[worker] s.f.Cache = &ssaCaches[worker]
s.f.Cache.Reset() s.f.Cache.Reset()
s.f.DebugTest = s.f.DebugHashMatch("GOSSAHASH", name)
s.f.Name = name s.f.Name = name
s.f.DebugTest = s.f.DebugHashMatch("GOSSAHASH")
s.f.PrintOrHtmlSSA = printssa s.f.PrintOrHtmlSSA = printssa
if fn.Func.Pragma&Nosplit != 0 { if fn.Func.Pragma&Nosplit != 0 {
s.f.NoSplit = true s.f.NoSplit = true
@ -338,6 +342,10 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.panics = map[funcLine]*ssa.Block{} s.panics = map[funcLine]*ssa.Block{}
s.softFloat = s.config.SoftFloat s.softFloat = s.config.SoftFloat
// Allocate starting block
s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
s.f.Entry.Pos = fn.Pos
if printssa { if printssa {
s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDumpFile, s.f, ssaDumpCFG) s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDumpFile, s.f, ssaDumpCFG)
// TODO: generate and print a mapping from nodes to values and blocks // TODO: generate and print a mapping from nodes to values and blocks
@ -345,9 +353,6 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.f.HTMLWriter.WriteAST("AST", astBuf) s.f.HTMLWriter.WriteAST("AST", astBuf)
} }
// Allocate starting block
s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
// Allocate starting values // Allocate starting values
s.labels = map[string]*ssaLabel{} s.labels = map[string]*ssaLabel{}
s.labeledNodes = map[*Node]*ssaLabel{} s.labeledNodes = map[*Node]*ssaLabel{}
@ -647,6 +652,8 @@ type state struct {
lastDeferExit *ssa.Block // Entry block of last defer exit code we generated lastDeferExit *ssa.Block // Entry block of last defer exit code we generated
lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated
lastDeferCount int // Number of defers encountered at that point lastDeferCount int // Number of defers encountered at that point
prevCall *ssa.Value // the previous call; use this to tie results to the call op.
} }
type funcLine struct { type funcLine struct {
@ -801,6 +808,11 @@ func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.
return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1) return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1)
} }
// newValue2A adds a new value with two arguments and an aux value to the current block.
func (s *state) newValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value {
return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
}
// newValue2Apos adds a new value with two arguments and an aux value to the current block. // newValue2Apos adds a new value with two arguments and an aux value to the current block.
// isStmt determines whether the created values may be a statement or not // isStmt determines whether the created values may be a statement or not
// (i.e., false means never, yes means maybe). // (i.e., false means never, yes means maybe).
@ -1067,7 +1079,7 @@ func (s *state) stmt(n *Node) {
fallthrough fallthrough
case OCALLMETH, OCALLINTER: case OCALLMETH, OCALLINTER:
s.call(n, callNormal) s.callResult(n, callNormal)
if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class() == PFUNC { if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class() == PFUNC {
if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" || if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" ||
n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") { n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") {
@ -1099,10 +1111,10 @@ func (s *state) stmt(n *Node) {
if n.Esc == EscNever { if n.Esc == EscNever {
d = callDeferStack d = callDeferStack
} }
s.call(n.Left, d) s.callResult(n.Left, d)
} }
case OGO: case OGO:
s.call(n.Left, callGo) s.callResult(n.Left, callGo)
case OAS2DOTTYPE: case OAS2DOTTYPE:
res, resok := s.dottype(n.Right, true) res, resok := s.dottype(n.Right, true)
@ -2109,7 +2121,7 @@ func (s *state) expr(n *Node) *ssa.Value {
} }
// unsafe.Pointer <--> *T // unsafe.Pointer <--> *T
if to.Etype == TUNSAFEPTR && from.IsPtrShaped() || from.Etype == TUNSAFEPTR && to.IsPtrShaped() { if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
return v return v
} }
@ -2545,8 +2557,23 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.addr(n.Left) return s.addr(n.Left)
case ORESULT: case ORESULT:
addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset) if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall {
return s.load(n.Type, addr) // Do the old thing
addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
return s.load(n.Type, addr)
}
which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
if which == -1 {
// Do the old thing // TODO: Panic instead.
addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
return s.load(n.Type, addr)
}
if canSSAType(n.Type) {
return s.newValue1I(ssa.OpSelectN, n.Type, which, s.prevCall)
} else {
addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(n.Type), which, s.prevCall)
return s.load(n.Type, addr)
}
case ODEREF: case ODEREF:
p := s.exprPtr(n.Left, n.Bounded(), n.Pos) p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
@ -2706,8 +2733,7 @@ func (s *state) expr(n *Node) *ssa.Value {
fallthrough fallthrough
case OCALLINTER, OCALLMETH: case OCALLINTER, OCALLMETH:
a := s.call(n, callNormal) return s.callResult(n, callNormal)
return s.load(n.Type, a)
case OGETG: case OGETG:
return s.newValue1(ssa.OpGetG, n.Type, s.mem()) return s.newValue1(ssa.OpGetG, n.Type, s.mem())
@ -3580,8 +3606,7 @@ func init() {
addF("math", "FMA", addF("math", "FMA",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA { if !s.config.UseFMA {
a := s.call(n, callNormal) s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.vars[n] = s.load(types.Types[TFLOAT64], a)
return s.variable(n, types.Types[TFLOAT64]) return s.variable(n, types.Types[TFLOAT64])
} }
v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA) v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA)
@ -3602,8 +3627,7 @@ func init() {
// Call the pure Go version. // Call the pure Go version.
s.startBlock(bFalse) s.startBlock(bFalse)
a := s.call(n, callNormal) s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.vars[n] = s.load(types.Types[TFLOAT64], a)
s.endBlock().AddEdgeTo(bEnd) s.endBlock().AddEdgeTo(bEnd)
// Merge results. // Merge results.
@ -3614,8 +3638,7 @@ func init() {
addF("math", "FMA", addF("math", "FMA",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA { if !s.config.UseFMA {
a := s.call(n, callNormal) s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.vars[n] = s.load(types.Types[TFLOAT64], a)
return s.variable(n, types.Types[TFLOAT64]) return s.variable(n, types.Types[TFLOAT64])
} }
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb) addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb)
@ -3637,8 +3660,7 @@ func init() {
// Call the pure Go version. // Call the pure Go version.
s.startBlock(bFalse) s.startBlock(bFalse)
a := s.call(n, callNormal) s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.vars[n] = s.load(types.Types[TFLOAT64], a)
s.endBlock().AddEdgeTo(bEnd) s.endBlock().AddEdgeTo(bEnd)
// Merge results. // Merge results.
@ -3667,8 +3689,7 @@ func init() {
// Call the pure Go version. // Call the pure Go version.
s.startBlock(bFalse) s.startBlock(bFalse)
a := s.call(n, callNormal) s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
s.vars[n] = s.load(types.Types[TFLOAT64], a)
s.endBlock().AddEdgeTo(bEnd) s.endBlock().AddEdgeTo(bEnd)
// Merge results. // Merge results.
@ -3878,8 +3899,7 @@ func init() {
// Call the pure Go version. // Call the pure Go version.
s.startBlock(bFalse) s.startBlock(bFalse)
a := s.call(n, callNormal) s.vars[n] = s.callResult(n, callNormal) // types.Types[TINT]
s.vars[n] = s.load(types.Types[TINT], a)
s.endBlock().AddEdgeTo(bEnd) s.endBlock().AddEdgeTo(bEnd)
// Merge results. // Merge results.
@ -4206,7 +4226,7 @@ func (s *state) openDeferSave(n *Node, t *types.Type, val *ssa.Value) *ssa.Value
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argTemp, s.mem(), false) s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argTemp, s.mem(), false)
addrArgTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(argTemp.Type), argTemp, s.sp, s.mem(), false) addrArgTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(argTemp.Type), argTemp, s.sp, s.mem(), false)
} }
if types.Haspointers(t) { if t.HasPointers() {
// Since we may use this argTemp during exit depending on the // Since we may use this argTemp during exit depending on the
// deferBits, we must define it unconditionally on entry. // deferBits, we must define it unconditionally on entry.
// Therefore, we must make sure it is zeroed out in the entry // Therefore, we must make sure it is zeroed out in the entry
@ -4271,16 +4291,20 @@ func (s *state) openDeferExit() {
argStart := Ctxt.FixedFrameSize() argStart := Ctxt.FixedFrameSize()
fn := r.n.Left fn := r.n.Left
stksize := fn.Type.ArgWidth() stksize := fn.Type.ArgWidth()
var ACArgs []ssa.Param
var ACResults []ssa.Param
if r.rcvr != nil { if r.rcvr != nil {
// rcvr in case of OCALLINTER // rcvr in case of OCALLINTER
v := s.load(r.rcvr.Type.Elem(), r.rcvr) v := s.load(r.rcvr.Type.Elem(), r.rcvr)
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
s.store(types.Types[TUINTPTR], addr, v) s.store(types.Types[TUINTPTR], addr, v)
} }
for j, argAddrVal := range r.argVals { for j, argAddrVal := range r.argVals {
f := getParam(r.n, j) f := getParam(r.n, j)
pt := types.NewPtr(f.Type) pt := types.NewPtr(f.Type)
addr := s.constOffPtrSP(pt, argStart+f.Offset) addr := s.constOffPtrSP(pt, argStart+f.Offset)
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart + f.Offset)})
if !canSSAType(f.Type) { if !canSSAType(f.Type) {
s.move(f.Type, addr, argAddrVal) s.move(f.Type, addr, argAddrVal)
} else { } else {
@ -4293,10 +4317,10 @@ func (s *state) openDeferExit() {
v := s.load(r.closure.Type.Elem(), r.closure) v := s.load(r.closure.Type.Elem(), r.closure)
s.maybeNilCheckClosure(v, callDefer) s.maybeNilCheckClosure(v, callDefer)
codeptr := s.rawLoad(types.Types[TUINTPTR], v) codeptr := s.rawLoad(types.Types[TUINTPTR], v)
call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, v, s.mem()) call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, v, s.mem())
} else { } else {
// Do a static call if the original call was a static function or method // Do a static call if the original call was a static function or method
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn.Sym.Linksym(), s.mem()) call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem())
} }
call.AuxInt = stksize call.AuxInt = stksize
s.vars[&memVar] = call s.vars[&memVar] = call
@ -4308,39 +4332,59 @@ func (s *state) openDeferExit() {
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false) s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false)
} }
if r.rcvrNode != nil { if r.rcvrNode != nil {
if types.Haspointers(r.rcvrNode.Type) { if r.rcvrNode.Type.HasPointers() {
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.rcvrNode, s.mem(), false) s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.rcvrNode, s.mem(), false)
} }
} }
for _, argNode := range r.argNodes { for _, argNode := range r.argNodes {
if types.Haspointers(argNode.Type) { if argNode.Type.HasPointers() {
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argNode, s.mem(), false) s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argNode, s.mem(), false)
} }
} }
if i == len(s.openDefers)-1 {
// Record the call of the first defer. This will be used
// to set liveness info for the deferreturn (which is also
// used for any location that causes a runtime panic)
s.f.LastDeferExit = call
}
s.endBlock() s.endBlock()
s.startBlock(bEnd) s.startBlock(bEnd)
} }
} }
func (s *state) callResult(n *Node, k callKind) *ssa.Value {
return s.call(n, k, false)
}
func (s *state) callAddr(n *Node, k callKind) *ssa.Value {
return s.call(n, k, true)
}
// Calls the function n using the specified call type. // Calls the function n using the specified call type.
// Returns the address of the return value (or nil if none). // Returns the address of the return value (or nil if none).
func (s *state) call(n *Node, k callKind) *ssa.Value { func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
s.prevCall = nil
var sym *types.Sym // target symbol (if static) var sym *types.Sym // target symbol (if static)
var closure *ssa.Value // ptr to closure to run (if dynamic) var closure *ssa.Value // ptr to closure to run (if dynamic)
var codeptr *ssa.Value // ptr to target code (if dynamic) var codeptr *ssa.Value // ptr to target code (if dynamic)
var rcvr *ssa.Value // receiver to set var rcvr *ssa.Value // receiver to set
fn := n.Left fn := n.Left
var ACArgs []ssa.Param
var ACResults []ssa.Param
var callArgs []*ssa.Value
res := n.Left.Type.Results()
if k == callNormal {
nf := res.NumFields()
for i := 0; i < nf; i++ {
fp := res.Field(i)
ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + Ctxt.FixedFrameSize())})
}
}
testLateExpansion := false
switch n.Op { switch n.Op {
case OCALLFUNC: case OCALLFUNC:
if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC { if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC {
sym = fn.Sym sym = fn.Sym
if !returnResultAddr && strings.Contains(sym.Name, "testLateExpansion") {
testLateExpansion = true
}
break break
} }
closure = s.expr(fn) closure = s.expr(fn)
@ -4355,6 +4399,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
} }
if k == callNormal { if k == callNormal {
sym = fn.Sym sym = fn.Sym
if !returnResultAddr && strings.Contains(sym.Name, "testLateExpansion") {
testLateExpansion = true
}
break break
} }
closure = s.getMethodClosure(fn) closure = s.getMethodClosure(fn)
@ -4434,10 +4481,12 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// Call runtime.deferprocStack with pointer to _defer record. // Call runtime.deferprocStack with pointer to _defer record.
arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize()) arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
s.store(types.Types[TUINTPTR], arg0, addr) s.store(types.Types[TUINTPTR], arg0, addr)
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem()) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())})
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults), s.mem())
if stksize < int64(Widthptr) { if stksize < int64(Widthptr) {
// We need room for both the call to deferprocStack and the call to // We need room for both the call to deferprocStack and the call to
// the deferred function. // the deferred function.
// TODO Revisit this if/when we pass args in registers.
stksize = int64(Widthptr) stksize = int64(Widthptr)
} }
call.AuxInt = stksize call.AuxInt = stksize
@ -4449,10 +4498,20 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
if k != callNormal { if k != callNormal {
// Write argsize and closure (args to newproc/deferproc). // Write argsize and closure (args to newproc/deferproc).
argsize := s.constInt32(types.Types[TUINT32], int32(stksize)) argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINT32], Offset: int32(argStart)})
s.store(types.Types[TUINT32], addr, argsize) if testLateExpansion {
addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr)) callArgs = append(callArgs, argsize)
s.store(types.Types[TUINTPTR], addr, closure) } else {
addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
s.store(types.Types[TUINT32], addr, argsize)
}
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart) + int32(Widthptr)})
if testLateExpansion {
callArgs = append(callArgs, closure)
} else {
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
s.store(types.Types[TUINTPTR], addr, closure)
}
stksize += 2 * int64(Widthptr) stksize += 2 * int64(Widthptr)
argStart += 2 * int64(Widthptr) argStart += 2 * int64(Widthptr)
} }
@ -4460,7 +4519,12 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// Set receiver (for interface calls). // Set receiver (for interface calls).
if rcvr != nil { if rcvr != nil {
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart) addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
s.store(types.Types[TUINTPTR], addr, rcvr) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
if testLateExpansion {
callArgs = append(callArgs, rcvr)
} else {
s.store(types.Types[TUINTPTR], addr, rcvr)
}
} }
// Write args. // Write args.
@ -4468,20 +4532,26 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
args := n.Rlist.Slice() args := n.Rlist.Slice()
if n.Op == OCALLMETH { if n.Op == OCALLMETH {
f := t.Recv() f := t.Recv()
s.storeArg(args[0], f.Type, argStart+f.Offset) ACArg, arg := s.putArg(args[0], f.Type, argStart+f.Offset, testLateExpansion)
ACArgs = append(ACArgs, ACArg)
callArgs = append(callArgs, arg)
args = args[1:] args = args[1:]
} }
for i, n := range args { for i, n := range args {
f := t.Params().Field(i) f := t.Params().Field(i)
s.storeArg(n, f.Type, argStart+f.Offset) ACArg, arg := s.putArg(n, f.Type, argStart+f.Offset, testLateExpansion)
ACArgs = append(ACArgs, ACArg)
callArgs = append(callArgs, arg)
} }
callArgs = append(callArgs, s.mem())
// call target // call target
switch { switch {
case k == callDefer: case k == callDefer:
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem()) call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferproc, ACArgs, ACResults), s.mem())
case k == callGo: case k == callGo:
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem()) call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(newproc, ACArgs, ACResults), s.mem())
case closure != nil: case closure != nil:
// rawLoad because loading the code pointer from a // rawLoad because loading the code pointer from a
// closure is always safe, but IsSanitizerSafeAddr // closure is always safe, but IsSanitizerSafeAddr
@ -4489,17 +4559,35 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
// critical that we not clobber any arguments already // critical that we not clobber any arguments already
// stored onto the stack. // stored onto the stack.
codeptr = s.rawLoad(types.Types[TUINTPTR], closure) codeptr = s.rawLoad(types.Types[TUINTPTR], closure)
call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem()) call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem())
case codeptr != nil: case codeptr != nil:
call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem()) call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem())
case sym != nil: case sym != nil:
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem()) if testLateExpansion {
var tys []*types.Type
aux := ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults)
for i := int64(0); i < aux.NResults(); i++ {
tys = append(tys, aux.TypeOfResult(i))
}
tys = append(tys, types.TypeMem)
call = s.newValue0A(ssa.OpStaticLECall, types.NewResults(tys), aux)
call.AddArgs(callArgs...)
} else {
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem())
}
default: default:
s.Fatalf("bad call type %v %v", n.Op, n) s.Fatalf("bad call type %v %v", n.Op, n)
} }
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
} }
s.vars[&memVar] = call if testLateExpansion {
s.prevCall = call
s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
} else {
s.vars[&memVar] = call
}
// Insert OVARLIVE nodes
s.stmtList(n.Nbody)
// Finish block for defers // Finish block for defers
if k == callDefer || k == callDeferStack { if k == callDefer || k == callDeferStack {
@ -4517,13 +4605,19 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
s.startBlock(bNext) s.startBlock(bNext)
} }
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.
return nil return nil
} }
fp := res.Field(0) fp := res.Field(0)
return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()) if returnResultAddr {
return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize())
}
if testLateExpansion {
return s.newValue1I(ssa.OpSelectN, fp.Type, 0, call)
}
return s.load(n.Type, s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()))
} }
// maybeNilCheckClosure checks if a nil check of a closure is needed in some // maybeNilCheckClosure checks if a nil check of a closure is needed in some
@ -4621,7 +4715,17 @@ func (s *state) addr(n *Node) *ssa.Value {
} }
case ORESULT: case ORESULT:
// load return from callee // load return from callee
return s.constOffPtrSP(t, n.Xoffset) if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall {
return s.constOffPtrSP(t, n.Xoffset)
}
which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
if which == -1 {
// Do the old thing // TODO: Panic instead.
return s.constOffPtrSP(t, n.Xoffset)
}
x := s.newValue1I(ssa.OpSelectNAddr, t, which, s.prevCall)
return x
case OINDEX: case OINDEX:
if n.Left.Type.IsSlice() { if n.Left.Type.IsSlice() {
a := s.expr(n.Left) a := s.expr(n.Left)
@ -4652,7 +4756,7 @@ func (s *state) addr(n *Node) *ssa.Value {
addr := s.addr(n.Left) addr := s.addr(n.Left)
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
case OCALLFUNC, OCALLINTER, OCALLMETH: case OCALLFUNC, OCALLINTER, OCALLMETH:
return s.call(n, callNormal) return s.callAddr(n, callNormal)
case ODOTTYPE: case ODOTTYPE:
v, _ := s.dottype(n, false) v, _ := s.dottype(n, false)
if v.Op != ssa.OpLoad { if v.Op != ssa.OpLoad {
@ -4911,20 +5015,32 @@ func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value {
// The call is added to the end of the current block. // The call is added to the end of the current block.
// If returns is false, the block is marked as an exit block. // If returns is false, the block is marked as an exit block.
func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value { func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value {
s.prevCall = nil
// Write args to the stack // Write args to the stack
off := Ctxt.FixedFrameSize() off := Ctxt.FixedFrameSize()
var ACArgs []ssa.Param
var ACResults []ssa.Param
for _, arg := range args { for _, arg := range args {
t := arg.Type t := arg.Type
off = Rnd(off, t.Alignment()) off = Rnd(off, t.Alignment())
ptr := s.constOffPtrSP(t.PtrTo(), off) ptr := s.constOffPtrSP(t.PtrTo(), off)
size := t.Size() size := t.Size()
ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
s.store(t, ptr, arg) s.store(t, ptr, arg)
off += size off += size
} }
off = Rnd(off, int64(Widthreg)) off = Rnd(off, int64(Widthreg))
// Accumulate results types and offsets
offR := off
for _, t := range results {
offR = Rnd(offR, t.Alignment())
ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)})
offR += t.Size()
}
// Issue call // Issue call
call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn, s.mem()) call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn, ACArgs, ACResults), s.mem())
s.vars[&memVar] = call s.vars[&memVar] = call
if !returns { if !returns {
@ -4959,7 +5075,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask, leftIsStmt bool) { func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask, leftIsStmt bool) {
s.instrument(t, left, true) s.instrument(t, left, true)
if skip == 0 && (!types.Haspointers(t) || ssa.IsStackAddr(left)) { if skip == 0 && (!t.HasPointers() || ssa.IsStackAddr(left)) {
// Known to not have write barrier. Store the whole type. // Known to not have write barrier. Store the whole type.
s.vars[&memVar] = s.newValue3Apos(ssa.OpStore, types.TypeMem, t, left, right, s.mem(), leftIsStmt) s.vars[&memVar] = s.newValue3Apos(ssa.OpStore, types.TypeMem, t, left, right, s.mem(), leftIsStmt)
return return
@ -4971,7 +5087,7 @@ func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask,
// TODO: if the writebarrier pass knows how to reorder stores, // TODO: if the writebarrier pass knows how to reorder stores,
// we can do a single store here as long as skip==0. // we can do a single store here as long as skip==0.
s.storeTypeScalars(t, left, right, skip) s.storeTypeScalars(t, left, right, skip)
if skip&skipPtr == 0 && types.Haspointers(t) { if skip&skipPtr == 0 && t.HasPointers() {
s.storeTypePtrs(t, left, right) s.storeTypePtrs(t, left, right)
} }
} }
@ -5043,7 +5159,7 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
n := t.NumFields() n := t.NumFields()
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
ft := t.FieldType(i) ft := t.FieldType(i)
if !types.Haspointers(ft) { if !ft.HasPointers() {
continue continue
} }
addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left) addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
@ -5059,8 +5175,21 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
} }
} }
func (s *state) storeArg(n *Node, t *types.Type, off int64) { // putArg evaluates n for the purpose of passing it as an argument to a function and returns the corresponding Param for the call.
s.storeArgWithBase(n, t, s.sp, off) // If forLateExpandedCall is true, it returns the argument value to pass to the call operation.
// If forLateExpandedCall is false, then the value is stored at the specified stack offset, and the returned value is nil.
func (s *state) putArg(n *Node, t *types.Type, off int64, forLateExpandedCall bool) (ssa.Param, *ssa.Value) {
var a *ssa.Value
if forLateExpandedCall {
if !canSSAType(t) {
a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem())
} else {
a = s.expr(n)
}
} else {
s.storeArgWithBase(n, t, s.sp, off)
}
return ssa.Param{Type: t, Offset: int32(off)}, a
} }
func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) { func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) {
@ -5807,11 +5936,6 @@ type SSAGenState struct {
// wasm: The number of values on the WebAssembly stack. This is only used as a safeguard. // wasm: The number of values on the WebAssembly stack. This is only used as a safeguard.
OnWasmStackSkipped int OnWasmStackSkipped int
// Liveness index for the first function call in the final defer exit code
// path that we generated. All defer functions and args should be live at
// this point. This will be used to set the liveness for the deferreturn.
lastDeferLiveness LivenessIndex
} }
// Prog appends a new Prog. // Prog appends a new Prog.
@ -6056,12 +6180,6 @@ func genssa(f *ssa.Func, pp *Progs) {
// instruction. // instruction.
s.pp.nextLive = s.livenessMap.Get(v) s.pp.nextLive = s.livenessMap.Get(v)
// Remember the liveness index of the first defer call of
// the last defer exit
if v.Block.Func.LastDeferExit != nil && v == v.Block.Func.LastDeferExit {
s.lastDeferLiveness = s.pp.nextLive
}
// Special case for first line in function; move it to the start. // Special case for first line in function; move it to the start.
if firstPos != src.NoXPos { if firstPos != src.NoXPos {
s.SetPos(firstPos) s.SetPos(firstPos)
@ -6122,7 +6240,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// When doing open-coded defers, generate a disconnected call to // When doing open-coded defers, generate a disconnected call to
// deferreturn and a return. This will be used to during panic // deferreturn and a return. This will be used to during panic
// recovery to unwind the stack and return back to the runtime. // recovery to unwind the stack and return back to the runtime.
s.pp.nextLive = s.lastDeferLiveness s.pp.nextLive = s.livenessMap.deferreturn
gencallret(pp, Deferreturn) gencallret(pp, Deferreturn)
} }
@ -6195,7 +6313,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// Resolve branches, and relax DefaultStmt into NotStmt // Resolve branches, and relax DefaultStmt into NotStmt
for _, br := range s.Branches { for _, br := range s.Branches {
br.P.To.Val = s.bstart[br.B.ID] br.P.To.SetTarget(s.bstart[br.B.ID])
if br.P.Pos.IsStmt() != src.PosIsStmt { if br.P.Pos.IsStmt() != src.PosIsStmt {
br.P.Pos = br.P.Pos.WithNotStmt() br.P.Pos = br.P.Pos.WithNotStmt()
} else if v0 := br.B.FirstPossibleStmtValue(); v0 != nil && v0.Pos.Line() == br.P.Pos.Line() && v0.Pos.IsStmt() == src.PosIsStmt { } else if v0 := br.B.FirstPossibleStmtValue(); v0 != nil && v0.Pos.Line() == br.P.Pos.Line() && v0.Pos.IsStmt() == src.PosIsStmt {
@ -6366,6 +6484,9 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
} }
// Add symbol's offset from its base register. // Add symbol's offset from its base register.
switch n := v.Aux.(type) { switch n := v.Aux.(type) {
case *ssa.AuxCall:
a.Name = obj.NAME_EXTERN
a.Sym = n.Fn
case *obj.LSym: case *obj.LSym:
a.Name = obj.NAME_EXTERN a.Name = obj.NAME_EXTERN
a.Sym = n a.Sym = n
@ -6552,10 +6673,10 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
} else { } else {
p.Pos = v.Pos.WithNotStmt() p.Pos = v.Pos.WithNotStmt()
} }
if sym, ok := v.Aux.(*obj.LSym); ok { if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN p.To.Name = obj.NAME_EXTERN
p.To.Sym = sym p.To.Sym = sym.Fn
} else { } else {
// TODO(mdempsky): Can these differences be eliminated? // TODO(mdempsky): Can these differences be eliminated?
switch thearch.LinkArch.Family { switch thearch.LinkArch.Family {
@ -6578,12 +6699,14 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) {
idx := s.livenessMap.Get(v) idx := s.livenessMap.Get(v)
if !idx.StackMapValid() { if !idx.StackMapValid() {
// See Liveness.hasStackMap. // See Liveness.hasStackMap.
if sym, _ := v.Aux.(*obj.LSym); !(sym == typedmemclr || sym == typedmemmove) { if sym, ok := v.Aux.(*ssa.AuxCall); !ok || !(sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
Fatalf("missing stack map index for %v", v.LongString()) Fatalf("missing stack map index for %v", v.LongString())
} }
} }
if sym, _ := v.Aux.(*obj.LSym); sym == Deferreturn { call, ok := v.Aux.(*ssa.AuxCall)
if ok && call.Fn == Deferreturn {
// Deferred calls will appear to be returning to // Deferred calls will appear to be returning to
// the CALL deferreturn(SB) that we are about to emit. // the CALL deferreturn(SB) that we are about to emit.
// However, the stack trace code will show the line // However, the stack trace code will show the line
@ -6595,11 +6718,11 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) {
thearch.Ginsnopdefer(s.pp) thearch.Ginsnopdefer(s.pp)
} }
if sym, ok := v.Aux.(*obj.LSym); ok { if ok {
// Record call graph information for nowritebarrierrec // Record call graph information for nowritebarrierrec
// analysis. // analysis.
if nowritebarrierrecCheck != nil { if nowritebarrierrecCheck != nil {
nowritebarrierrecCheck.recordCall(s.pp.curfn, sym, v.Pos) nowritebarrierrecCheck.recordCall(s.pp.curfn, call.Fn, v.Pos)
} }
} }
@ -6879,6 +7002,10 @@ func (e *ssafn) SetWBPos(pos src.XPos) {
e.curfn.Func.setWBPos(pos) e.curfn.Func.setWBPos(pos)
} }
func (e *ssafn) MyImportPath() string {
return myimportpath
}
func (n *Node) Typ() *types.Type { func (n *Node) Typ() *types.Type {
return n.Type return n.Type
} }

View file

@ -271,13 +271,6 @@ func autolabel(prefix string) *types.Sym {
return lookupN(prefix, int(n)) return lookupN(prefix, int(n))
} }
func restrictlookup(name string, pkg *types.Pkg) *types.Sym {
if !types.IsExported(name) && pkg != localpkg {
yyerror("cannot refer to unexported name %s.%s", pkg.Name, name)
}
return pkg.Lookup(name)
}
// find all the exported symbols in package opkg // find all the exported symbols in package opkg
// and make them available in the current package // and make them available in the current package
func importdot(opkg *types.Pkg, pack *Node) { func importdot(opkg *types.Pkg, pack *Node) {
@ -696,14 +689,14 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
// (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't. // (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't.
if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() { if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() {
if why != nil { if why != nil {
*why = fmt.Sprintf(":\n\t%v is go:notinheap, but %v is not", dst.Elem(), src.Elem()) *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
} }
return OXXX return OXXX
} }
// (b) Disallow string to []T where T is go:notinheap. // (b) Disallow string to []T where T is go:notinheap.
if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) { if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Etype == types.Bytetype.Etype || dst.Elem().Etype == types.Runetype.Etype) {
if why != nil { if why != nil {
*why = fmt.Sprintf(":\n\t%v is go:notinheap", dst.Elem()) *why = fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
} }
return OXXX return OXXX
} }
@ -788,12 +781,12 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
} }
// 8. src is a pointer or uintptr and dst is unsafe.Pointer. // 8. src is a pointer or uintptr and dst is unsafe.Pointer.
if (src.IsPtr() || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR { if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
return OCONVNOP return OCONVNOP
} }
// 9. src is unsafe.Pointer and dst is a pointer or uintptr. // 9. src is unsafe.Pointer and dst is a pointer or uintptr.
if src.Etype == TUNSAFEPTR && (dst.IsPtr() || dst.Etype == TUINTPTR) { if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
return OCONVNOP return OCONVNOP
} }
@ -935,16 +928,20 @@ func (o Op) IsSlice3() bool {
return false return false
} }
// slicePtrLen extracts the pointer and length from a slice. // backingArrayPtrLen extracts the pointer and length from a slice or string.
// This constructs two nodes referring to n, so n must be a cheapexpr. // This constructs two nodes referring to n, so n must be a cheapexpr.
func (n *Node) slicePtrLen() (ptr, len *Node) { func (n *Node) backingArrayPtrLen() (ptr, len *Node) {
var init Nodes var init Nodes
c := cheapexpr(n, &init) c := cheapexpr(n, &init)
if c != n || init.Len() != 0 { if c != n || init.Len() != 0 {
Fatalf("slicePtrLen not cheap: %v", n) Fatalf("backingArrayPtrLen not cheap: %v", n)
} }
ptr = nod(OSPTR, n, nil) ptr = nod(OSPTR, n, nil)
ptr.Type = n.Type.Elem().PtrTo() if n.Type.IsString() {
ptr.Type = types.Types[TUINT8].PtrTo()
} else {
ptr.Type = n.Type.Elem().PtrTo()
}
len = nod(OLEN, n, nil) len = nod(OLEN, n, nil)
len.Type = types.Types[TINT] len.Type = types.Types[TINT]
return ptr, len return ptr, len
@ -1550,7 +1547,6 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
tfn.List.Set(structargs(method.Type.Params(), true)) tfn.List.Set(structargs(method.Type.Params(), true))
tfn.Rlist.Set(structargs(method.Type.Results(), false)) tfn.Rlist.Set(structargs(method.Type.Results(), false))
disableExport(newnam)
fn := dclfunc(newnam, tfn) fn := dclfunc(newnam, tfn)
fn.Func.SetDupok(true) fn.Func.SetDupok(true)
@ -1623,7 +1619,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
escapeFuncs([]*Node{fn}, false) escapeFuncs([]*Node{fn}, false)
Curfn = nil Curfn = nil
funccompile(fn) xtop = append(xtop, fn)
} }
func paramNnames(ft *types.Type) []*Node { func paramNnames(ft *types.Type) []*Node {
@ -1638,8 +1634,7 @@ func hashmem(t *types.Type) *Node {
sym := Runtimepkg.Lookup("memhash") sym := Runtimepkg.Lookup("memhash")
n := newname(sym) n := newname(sym)
n.SetClass(PFUNC) setNodeNameFunc(n)
n.Sym.SetFunc(true)
n.Type = functype(nil, []*Node{ n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)), anonfield(types.NewPtr(t)),
anonfield(types.Types[TUINTPTR]), anonfield(types.Types[TUINTPTR]),

View file

@ -141,8 +141,8 @@ const (
nodeInitorder, _ // tracks state during init1; two bits nodeInitorder, _ // tracks state during init1; two bits
_, _ // second nodeInitorder bit _, _ // second nodeInitorder bit
_, nodeHasBreak _, nodeHasBreak
_, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only _, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
_, nodeImplicit _, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
_, nodeIsDDD // is the argument variadic _, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this _, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from := _, nodeColas // OAS resulting from :=
@ -359,7 +359,6 @@ const (
nameReadonly nameReadonly
nameByval // is the variable captured by value or by reference nameByval // is the variable captured by value or by reference
nameNeedzero // if it contains pointers, needs to be zeroed on function entry nameNeedzero // if it contains pointers, needs to be zeroed on function entry
nameKeepalive // mark value live across unknown assembly call
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap) nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
nameUsed // for variable declared and not used error nameUsed // for variable declared and not used error
nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original at n.Name.Defn
@ -376,7 +375,6 @@ func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 } func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
func (n *Name) Byval() bool { return n.flags&nameByval != 0 } func (n *Name) Byval() bool { return n.flags&nameByval != 0 }
func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 } func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
func (n *Name) Keepalive() bool { return n.flags&nameKeepalive != 0 }
func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 } func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
func (n *Name) Used() bool { return n.flags&nameUsed != 0 } func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 } func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
@ -392,7 +390,6 @@ func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) } func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) }
func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) } func (n *Name) SetByval(b bool) { n.flags.set(nameByval, b) }
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) } func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
func (n *Name) SetKeepalive(b bool) { n.flags.set(nameKeepalive, b) }
func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) } func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) } func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) } func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
@ -692,6 +689,7 @@ const (
// Prior to walk, they are: Left(List), where List is all regular arguments. // Prior to walk, they are: Left(List), where List is all regular arguments.
// After walk, List is a series of assignments to temporaries, // After walk, List is a series of assignments to temporaries,
// and Rlist is an updated set of arguments. // and Rlist is an updated set of arguments.
// Nbody is all OVARLIVE nodes that are attached to OCALLxxx.
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797. // TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
OCALLFUNC // Left(List/Rlist) (function call f(args)) OCALLFUNC // Left(List/Rlist) (function call f(args))
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args)) OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
@ -718,7 +716,7 @@ const (
ODCLCONST // const pi = 3.14 ODCLCONST // const pi = 3.14
ODCLTYPE // type Int int or type Int = int ODCLTYPE // type Int int or type Int = int
ODELETE // delete(Left, Right) ODELETE // delete(List)
ODOT // Left.Sym (Left is of struct type) ODOT // Left.Sym (Left is of struct type)
ODOTPTR // Left.Sym (Left is of pointer to struct type) ODOTPTR // Left.Sym (Left is of pointer to struct type)
ODOTMETH // Left.Sym (Left is non-interface, Right is method name) ODOTMETH // Left.Sym (Left is non-interface, Right is method name)

View file

@ -151,8 +151,8 @@ var _typekind = []string{
} }
func typekind(t *types.Type) string { func typekind(t *types.Type) string {
if t.IsSlice() { if t.IsUntyped() {
return "slice" return fmt.Sprintf("%v", t)
} }
et := t.Etype et := t.Etype
if int(et) < len(_typekind) { if int(et) < len(_typekind) {
@ -471,10 +471,10 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
} }
if l.Type.NotInHeap() { if l.Type.NotInHeap() {
yyerror("go:notinheap map key not allowed") yyerror("incomplete (or unallocatable) map key not allowed")
} }
if r.Type.NotInHeap() { if r.Type.NotInHeap() {
yyerror("go:notinheap map value not allowed") yyerror("incomplete (or unallocatable) map value not allowed")
} }
setTypeNode(n, types.NewMap(l.Type, r.Type)) setTypeNode(n, types.NewMap(l.Type, r.Type))
@ -491,7 +491,7 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
} }
if l.Type.NotInHeap() { if l.Type.NotInHeap() {
yyerror("chan of go:notinheap type not allowed") yyerror("chan of incomplete (or unallocatable) type not allowed")
} }
setTypeNode(n, types.NewChan(l.Type, n.TChanDir())) setTypeNode(n, types.NewChan(l.Type, n.TChanDir()))
@ -623,10 +623,29 @@ func typecheck1(n *Node, top int) (res *Node) {
// no defaultlit for left // no defaultlit for left
// the outer context gives the type // the outer context gives the type
n.Type = l.Type n.Type = l.Type
if (l.Type == types.Idealfloat || l.Type == types.Idealcomplex) && r.Op == OLITERAL {
n.Type = types.Idealint
}
break break
} }
// For "x == x && len(s)", it's better to report that "len(s)" (type int)
// can't be used with "&&" than to report that "x == x" (type untyped bool)
// can't be converted to int (see issue #41500).
if n.Op == OANDAND || n.Op == OOROR {
if !n.Left.Type.IsBoolean() {
yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Left.Type))
n.Type = nil
return n
}
if !n.Right.Type.IsBoolean() {
yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Right.Type))
n.Type = nil
return n
}
}
// ideal mixed with non-ideal // ideal mixed with non-ideal
l, r = defaultlit2(l, r, false) l, r = defaultlit2(l, r, false)
@ -713,7 +732,10 @@ func typecheck1(n *Node, top int) (res *Node) {
} }
} }
if !okfor[op][et] { if t.Etype == TIDEAL {
t = mixUntyped(l.Type, r.Type)
}
if dt := defaultType(t); !okfor[op][dt.Etype] {
yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t)) yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
n.Type = nil n.Type = nil
return n return n
@ -753,15 +775,7 @@ func typecheck1(n *Node, top int) (res *Node) {
} }
} }
t = l.Type
if iscmp[n.Op] { if iscmp[n.Op] {
// TIDEAL includes complex constant, but only OEQ and ONE are defined for complex,
// so check that the n.op is available for complex here before doing evconst.
if !okfor[n.Op][TCOMPLEX128] && (Isconst(l, CTCPLX) || Isconst(r, CTCPLX)) {
yyerror("invalid operation: %v (operator %v not defined on untyped complex)", n, n.Op)
n.Type = nil
return n
}
evconst(n) evconst(n)
t = types.Idealbool t = types.Idealbool
if n.Op != OLITERAL { if n.Op != OLITERAL {
@ -808,8 +822,8 @@ func typecheck1(n *Node, top int) (res *Node) {
n.Type = nil n.Type = nil
return n return n
} }
if !okfor[n.Op][t.Etype] { if !okfor[n.Op][defaultType(t).Etype] {
yyerror("invalid operation: %v %v", n.Op, t) yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(t))
n.Type = nil n.Type = nil
return n return n
} }
@ -1678,7 +1692,7 @@ func typecheck1(n *Node, top int) (res *Node) {
} }
var why string var why string
n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why) n.Op = convertop(n.Left.Op == OLITERAL, t, n.Type, &why)
if n.Op == 0 { if n.Op == OXXX {
if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() { if !n.Diag() && !n.Type.Broke() && !n.Left.Diag() {
yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why) yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why)
n.SetDiag(true) n.SetDiag(true)
@ -2068,12 +2082,6 @@ func typecheck1(n *Node, top int) (res *Node) {
ok |= ctxStmt ok |= ctxStmt
n.Left = typecheck(n.Left, ctxType) n.Left = typecheck(n.Left, ctxType)
checkwidth(n.Left.Type) checkwidth(n.Left.Type)
if n.Left.Type != nil && n.Left.Type.NotInHeap() && n.Left.Name.Param.Pragma&NotInHeap == 0 {
// The type contains go:notinheap types, so it
// must be marked as such (alternatively, we
// could silently propagate go:notinheap).
yyerror("type %v must be go:notinheap", n.Left.Type)
}
} }
t := n.Type t := n.Type
@ -2667,7 +2675,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes,
return return
notenough: notenough:
if n == nil || !n.Diag() { if n == nil || (!n.Diag() && n.Type != nil) {
details := errorDetails(nl, tstruct, isddd) details := errorDetails(nl, tstruct, isddd)
if call != nil { if call != nil {
// call is the expression being called, not the overall call. // call is the expression being called, not the overall call.
@ -2708,13 +2716,13 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
return "" return ""
} }
} }
return fmt.Sprintf("\n\thave %s\n\twant %v", nl.retsigerr(isddd), tstruct) return fmt.Sprintf("\n\thave %s\n\twant %v", nl.sigerr(isddd), tstruct)
} }
// sigrepr is a type's representation to the outside world, // sigrepr is a type's representation to the outside world,
// in string representations of return signatures // in string representations of return signatures
// e.g in error messages about wrong arguments to return. // e.g in error messages about wrong arguments to return.
func sigrepr(t *types.Type) string { func sigrepr(t *types.Type, isddd bool) string {
switch t { switch t {
case types.Idealstring: case types.Idealstring:
return "string" return "string"
@ -2729,26 +2737,29 @@ func sigrepr(t *types.Type) string {
return "number" return "number"
} }
// Turn []T... argument to ...T for clearer error message.
if isddd {
if !t.IsSlice() {
Fatalf("bad type for ... argument: %v", t)
}
return "..." + t.Elem().String()
}
return t.String() return t.String()
} }
// retsigerr returns the signature of the types // sigerr returns the signature of the types at the call or return.
// at the respective return call site of a function. func (nl Nodes) sigerr(isddd bool) string {
func (nl Nodes) retsigerr(isddd bool) string {
if nl.Len() < 1 { if nl.Len() < 1 {
return "()" return "()"
} }
var typeStrings []string var typeStrings []string
for _, n := range nl.Slice() { for i, n := range nl.Slice() {
typeStrings = append(typeStrings, sigrepr(n.Type)) isdddArg := isddd && i == nl.Len()-1
typeStrings = append(typeStrings, sigrepr(n.Type, isdddArg))
} }
ddd := "" return fmt.Sprintf("(%s)", strings.Join(typeStrings, ", "))
if isddd {
ddd = "..."
}
return fmt.Sprintf("(%s%s)", strings.Join(typeStrings, ", "), ddd)
} }
// type check composite // type check composite
@ -3135,9 +3146,14 @@ func checkassign(stmt *Node, n *Node) {
return return
} }
if n.Op == ODOT && n.Left.Op == OINDEXMAP { switch {
case n.Op == ODOT && n.Left.Op == OINDEXMAP:
yyerror("cannot assign to struct field %v in map", n) yyerror("cannot assign to struct field %v in map", n)
} else { case (n.Op == OINDEX && n.Left.Type.IsString()) || n.Op == OSLICESTR:
yyerror("cannot assign to %v (strings are immutable)", n)
case n.Op == OLITERAL && n.Sym != nil && n.isGoConst():
yyerror("cannot assign to %v (declared const)", n)
default:
yyerror("cannot assign to %v", n) yyerror("cannot assign to %v", n)
} }
n.Type = nil n.Type = nil

View file

@ -6,6 +6,7 @@ package gc
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi" "cmd/internal/objabi"
"cmd/internal/sys" "cmd/internal/sys"
"encoding/binary" "encoding/binary"
@ -230,6 +231,13 @@ func walkstmt(n *Node) *Node {
case OCOPY: case OCOPY:
n.Left = copyany(n.Left, &n.Ninit, true) n.Left = copyany(n.Left, &n.Ninit, true)
case OCALLFUNC, OCALLMETH, OCALLINTER:
if n.Left.Nbody.Len() > 0 {
n.Left = wrapCall(n.Left, &n.Ninit)
} else {
n.Left = walkexpr(n.Left, &n.Ninit)
}
default: default:
n.Left = walkexpr(n.Left, &n.Ninit) n.Left = walkexpr(n.Left, &n.Ninit)
} }
@ -380,9 +388,9 @@ func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) {
switch { switch {
case from.Size() == 2 && from.Align == 2: case from.Size() == 2 && from.Align == 2:
return "convT16", false return "convT16", false
case from.Size() == 4 && from.Align == 4 && !types.Haspointers(from): case from.Size() == 4 && from.Align == 4 && !from.HasPointers():
return "convT32", false return "convT32", false
case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !types.Haspointers(from): case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !from.HasPointers():
return "convT64", false return "convT64", false
} }
if sc := from.SoleComponent(); sc != nil { if sc := from.SoleComponent(); sc != nil {
@ -396,12 +404,12 @@ func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) {
switch tkind { switch tkind {
case 'E': case 'E':
if !types.Haspointers(from) { if !from.HasPointers() {
return "convT2Enoptr", true return "convT2Enoptr", true
} }
return "convT2E", true return "convT2E", true
case 'I': case 'I':
if !types.Haspointers(from) { if !from.HasPointers() {
return "convT2Inoptr", true return "convT2Inoptr", true
} }
return "convT2I", true return "convT2I", true
@ -640,7 +648,7 @@ opswitch:
// x = append(...) // x = append(...)
r := n.Right r := n.Right
if r.Type.Elem().NotInHeap() { if r.Type.Elem().NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", r.Type.Elem()) yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", r.Type.Elem())
} }
switch { switch {
case isAppendOfMake(r): case isAppendOfMake(r):
@ -797,6 +805,10 @@ opswitch:
fromType := n.Left.Type fromType := n.Left.Type
toType := n.Type toType := n.Type
if !fromType.IsInterface() {
markTypeUsedInInterface(fromType)
}
// typeword generates the type word of the interface value. // typeword generates the type word of the interface value.
typeword := func() *Node { typeword := func() *Node {
if toType.IsEmptyInterface() { if toType.IsEmptyInterface() {
@ -949,11 +961,11 @@ opswitch:
case OCONV, OCONVNOP: case OCONV, OCONVNOP:
n.Left = walkexpr(n.Left, init) n.Left = walkexpr(n.Left, init)
if n.Op == OCONVNOP && checkPtr(Curfn, 1) { if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T if n.Type.IsPtr() && n.Left.Type.IsUnsafePtr() { // unsafe.Pointer to *T
n = walkCheckPtrAlignment(n, init, nil) n = walkCheckPtrAlignment(n, init, nil)
break break
} }
if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { // uintptr to unsafe.Pointer
n = walkCheckPtrArithmetic(n, init) n = walkCheckPtrArithmetic(n, init)
break break
} }
@ -968,6 +980,7 @@ opswitch:
case OANDNOT: case OANDNOT:
n.Left = walkexpr(n.Left, init) n.Left = walkexpr(n.Left, init)
n.Op = OAND n.Op = OAND
n.SetImplicit(true) // for walkCheckPtrArithmetic
n.Right = nod(OBITNOT, n.Right, nil) n.Right = nod(OBITNOT, n.Right, nil)
n.Right = typecheck(n.Right, ctxExpr) n.Right = typecheck(n.Right, ctxExpr)
n.Right = walkexpr(n.Right, init) n.Right = walkexpr(n.Right, init)
@ -1117,7 +1130,7 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init)) n.List.SetSecond(walkexpr(n.List.Second(), init))
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.IsUnsafePtr()
if checkSlice { if checkSlice {
n.Left.Left = walkexpr(n.Left.Left, init) n.Left.Left = walkexpr(n.Left.Left, init)
} else { } else {
@ -1150,6 +1163,9 @@ opswitch:
} }
case ONEW: case ONEW:
if n.Type.Elem().NotInHeap() {
yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type.Elem())
}
if n.Esc == EscNone { if n.Esc == EscNone {
if n.Type.Elem().Width >= maxImplicitStackVarSize { if n.Type.Elem().Width >= maxImplicitStackVarSize {
Fatalf("large ONEW with EscNone: %v", n) Fatalf("large ONEW with EscNone: %v", n)
@ -1318,6 +1334,9 @@ opswitch:
l = r l = r
} }
t := n.Type t := n.Type
if t.Elem().NotInHeap() {
yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
}
if n.Esc == EscNone { if n.Esc == EscNone {
if !isSmallMakeSlice(n) { if !isSmallMakeSlice(n) {
Fatalf("non-small OMAKESLICE with EscNone: %v", n) Fatalf("non-small OMAKESLICE with EscNone: %v", n)
@ -1359,10 +1378,6 @@ opswitch:
// When len and cap can fit into int, use makeslice instead of // When len and cap can fit into int, use makeslice instead of
// makeslice64, which is faster and shorter on 32 bit platforms. // makeslice64, which is faster and shorter on 32 bit platforms.
if t.Elem().NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
}
len, cap := l, r len, cap := l, r
fnname := "makeslice64" fnname := "makeslice64"
@ -1397,14 +1412,14 @@ opswitch:
t := n.Type t := n.Type
if t.Elem().NotInHeap() { if t.Elem().NotInHeap() {
Fatalf("%v is go:notinheap; heap allocation disallowed", t.Elem()) yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
} }
length := conv(n.Left, types.Types[TINT]) length := conv(n.Left, types.Types[TINT])
copylen := nod(OLEN, n.Right, nil) copylen := nod(OLEN, n.Right, nil)
copyptr := nod(OSPTR, n.Right, nil) copyptr := nod(OSPTR, n.Right, nil)
if !types.Haspointers(t.Elem()) && n.Bounded() { if !t.Elem().HasPointers() && n.Bounded() {
// When len(to)==len(from) and elements have no pointers: // When len(to)==len(from) and elements have no pointers:
// replace make+copy with runtime.mallocgc+runtime.memmove. // replace make+copy with runtime.mallocgc+runtime.memmove.
@ -1469,7 +1484,7 @@ opswitch:
} else { } else {
// slicebytetostring(*[32]byte, ptr *byte, n int) string // slicebytetostring(*[32]byte, ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init) n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen() ptr, len := n.Left.backingArrayPtrLen()
n = mkcall("slicebytetostring", n.Type, init, a, ptr, len) n = mkcall("slicebytetostring", n.Type, init, a, ptr, len)
} }
@ -1482,7 +1497,7 @@ opswitch:
} }
// slicebytetostringtmp(ptr *byte, n int) string // slicebytetostringtmp(ptr *byte, n int) string
n.Left = cheapexpr(n.Left, init) n.Left = cheapexpr(n.Left, init)
ptr, len := n.Left.slicePtrLen() ptr, len := n.Left.backingArrayPtrLen()
n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len) n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len)
case OSTR2BYTES: case OSTR2BYTES:
@ -1551,8 +1566,7 @@ opswitch:
if isStaticCompositeLiteral(n) && !canSSAType(n.Type) { if isStaticCompositeLiteral(n) && !canSSAType(n.Type) {
// n can be directly represented in the read-only data section. // n can be directly represented in the read-only data section.
// Make direct reference to the static data. See issue 12841. // Make direct reference to the static data. See issue 12841.
vstat := staticname(n.Type) vstat := readonlystaticname(n.Type)
vstat.MarkReadonly()
fixedlit(inInitFunction, initKindStatic, n, vstat, init) fixedlit(inInitFunction, initKindStatic, n, vstat, init)
n = vstat n = vstat
n = typecheck(n, ctxExpr) n = typecheck(n, ctxExpr)
@ -1605,6 +1619,12 @@ opswitch:
return n return n
} }
// markTypeUsedInInterface marks that type t is converted to an interface.
// This information is used in the linker in dead method elimination.
func markTypeUsedInInterface(t *types.Type) {
typenamesym(t).Linksym().Set(obj.AttrUsedInIface, true)
}
// rtconvfn returns the parameter and result types that will be used by a // rtconvfn returns the parameter and result types that will be used by a
// runtime function to convert from type src to type dst. The runtime function // runtime function to convert from type src to type dst. The runtime function
// name can be derived from the names of the returned types. // name can be derived from the names of the returned types.
@ -2001,9 +2021,6 @@ func walkprint(nn *Node, init *Nodes) *Node {
} }
func callnew(t *types.Type) *Node { func callnew(t *types.Type) *Node {
if t.NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", t)
}
dowidth(t) dowidth(t)
n := nod(ONEWOBJ, typename(t), nil) n := nod(ONEWOBJ, typename(t), nil)
n.Type = types.NewPtr(t) n.Type = types.NewPtr(t)
@ -2578,7 +2595,7 @@ func mapfast(t *types.Type) int {
} }
switch algtype(t.Key()) { switch algtype(t.Key()) {
case AMEM32: case AMEM32:
if !t.Key().HasHeapPointer() { if !t.Key().HasPointers() {
return mapfast32 return mapfast32
} }
if Widthptr == 4 { if Widthptr == 4 {
@ -2586,7 +2603,7 @@ func mapfast(t *types.Type) int {
} }
Fatalf("small pointer %v", t.Key()) Fatalf("small pointer %v", t.Key())
case AMEM64: case AMEM64:
if !t.Key().HasHeapPointer() { if !t.Key().HasPointers() {
return mapfast64 return mapfast64
} }
if Widthptr == 8 { if Widthptr == 8 {
@ -2733,7 +2750,7 @@ func appendslice(n *Node, init *Nodes) *Node {
nodes.Append(nod(OAS, s, nt)) nodes.Append(nod(OAS, s, nt))
var ncopy *Node var ncopy *Node
if elemtype.HasHeapPointer() { if elemtype.HasPointers() {
// copy(s[len(l1):], l2) // copy(s[len(l1):], l2)
nptr1 := nod(OSLICE, s, nil) nptr1 := nod(OSLICE, s, nil)
nptr1.Type = s.Type nptr1.Type = s.Type
@ -2747,36 +2764,25 @@ func appendslice(n *Node, init *Nodes) *Node {
// instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int // instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int
fn := syslook("typedslicecopy") fn := syslook("typedslicecopy")
fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem()) fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
ptr1, len1 := nptr1.slicePtrLen() ptr1, len1 := nptr1.backingArrayPtrLen()
ptr2, len2 := nptr2.slicePtrLen() ptr2, len2 := nptr2.backingArrayPtrLen()
ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), ptr1, len1, ptr2, len2) ncopy = mkcall1(fn, types.Types[TINT], &nodes, typename(elemtype), ptr1, len1, ptr2, len2)
} else if instrumenting && !compiling_runtime { } else if instrumenting && !compiling_runtime {
// rely on runtime to instrument copy. // rely on runtime to instrument:
// copy(s[len(l1):], l2) // copy(s[len(l1):], l2)
// l2 can be a slice or string.
nptr1 := nod(OSLICE, s, nil) nptr1 := nod(OSLICE, s, nil)
nptr1.Type = s.Type nptr1.Type = s.Type
nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil) nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
nptr1 = cheapexpr(nptr1, &nodes) nptr1 = cheapexpr(nptr1, &nodes)
nptr2 := l2 nptr2 := l2
if l2.Type.IsString() { ptr1, len1 := nptr1.backingArrayPtrLen()
// instantiate func slicestringcopy(toPtr *byte, toLen int, fr string) int ptr2, len2 := nptr2.backingArrayPtrLen()
fn := syslook("slicestringcopy")
ptr, len := nptr1.slicePtrLen()
str := nod(OCONVNOP, nptr2, nil)
str.Type = types.Types[TSTRING]
ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr, len, str)
} else {
// instantiate func slicecopy(to any, fr any, wid uintptr) int
fn := syslook("slicecopy")
fn = substArgTypes(fn, l1.Type.Elem(), l2.Type.Elem())
ptr1, len1 := nptr1.slicePtrLen()
ptr2, len2 := nptr2.slicePtrLen()
ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width))
}
fn := syslook("slicecopy")
fn = substArgTypes(fn, ptr1.Type.Elem(), ptr2.Type.Elem())
ncopy = mkcall1(fn, types.Types[TINT], &nodes, ptr1, len1, ptr2, len2, nodintconst(elemtype.Width))
} else { } else {
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil)) nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil))
@ -2854,7 +2860,7 @@ func isAppendOfMake(n *Node) bool {
// s = s[:n] // s = s[:n]
// lptr := &l1[0] // lptr := &l1[0]
// sptr := &s[0] // sptr := &s[0]
// if lptr == sptr || !hasPointers(T) { // if lptr == sptr || !T.HasPointers() {
// // growslice did not clear the whole underlying array (or did not get called) // // growslice did not clear the whole underlying array (or did not get called)
// hp := &s[len(l1)] // hp := &s[len(l1)]
// hn := l2 * sizeof(T) // hn := l2 * sizeof(T)
@ -2935,7 +2941,7 @@ func extendslice(n *Node, init *Nodes) *Node {
hn = conv(hn, types.Types[TUINTPTR]) hn = conv(hn, types.Types[TUINTPTR])
clrname := "memclrNoHeapPointers" clrname := "memclrNoHeapPointers"
hasPointers := types.Haspointers(elemtype) hasPointers := elemtype.HasPointers()
if hasPointers { if hasPointers {
clrname = "memclrHasPointers" clrname = "memclrHasPointers"
Curfn.Func.setWBPos(n.Pos) Curfn.Func.setWBPos(n.Pos)
@ -3071,32 +3077,29 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
// Also works if b is a string. // Also works if b is a string.
// //
func copyany(n *Node, init *Nodes, runtimecall bool) *Node { func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
if n.Left.Type.Elem().HasHeapPointer() { if n.Left.Type.Elem().HasPointers() {
Curfn.Func.setWBPos(n.Pos) Curfn.Func.setWBPos(n.Pos)
fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem()) fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init) n.Left = cheapexpr(n.Left, init)
ptrL, lenL := n.Left.slicePtrLen() ptrL, lenL := n.Left.backingArrayPtrLen()
n.Right = cheapexpr(n.Right, init) n.Right = cheapexpr(n.Right, init)
ptrR, lenR := n.Right.slicePtrLen() ptrR, lenR := n.Right.backingArrayPtrLen()
return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR) return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR)
} }
if runtimecall { if runtimecall {
if n.Right.Type.IsString() { // rely on runtime to instrument:
fn := syslook("slicestringcopy") // copy(n.Left, n.Right)
n.Left = cheapexpr(n.Left, init) // n.Right can be a slice or string.
ptr, len := n.Left.slicePtrLen()
str := nod(OCONVNOP, n.Right, nil) n.Left = cheapexpr(n.Left, init)
str.Type = types.Types[TSTRING] ptrL, lenL := n.Left.backingArrayPtrLen()
return mkcall1(fn, n.Type, init, ptr, len, str) n.Right = cheapexpr(n.Right, init)
} ptrR, lenR := n.Right.backingArrayPtrLen()
fn := syslook("slicecopy") fn := syslook("slicecopy")
fn = substArgTypes(fn, n.Left.Type.Elem(), n.Right.Type.Elem()) fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem())
n.Left = cheapexpr(n.Left, init)
ptrL, lenL := n.Left.slicePtrLen()
n.Right = cheapexpr(n.Right, init)
ptrR, lenR := n.Right.slicePtrLen()
return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width)) return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width))
} }
@ -3156,8 +3159,7 @@ func eqfor(t *types.Type) (n *Node, needsize bool) {
case ASPECIAL: case ASPECIAL:
sym := typesymprefix(".eq", t) sym := typesymprefix(".eq", t)
n := newname(sym) n := newname(sym)
n.SetClass(PFUNC) setNodeNameFunc(n)
n.Sym.SetFunc(true)
n.Type = functype(nil, []*Node{ n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)), anonfield(types.NewPtr(t)),
anonfield(types.NewPtr(t)), anonfield(types.NewPtr(t)),
@ -3848,6 +3850,14 @@ func candiscard(n *Node) bool {
// builtin(a1, a2, a3) // builtin(a1, a2, a3)
// }(x, y, z) // }(x, y, z)
// for print, println, and delete. // for print, println, and delete.
//
// Rewrite
// go f(x, y, uintptr(unsafe.Pointer(z)))
// into
// go func(a1, a2, a3) {
// builtin(a1, a2, uintptr(a3))
// }(x, y, unsafe.Pointer(z))
// for function contains unsafe-uintptr arguments.
var wrapCall_prgen int var wrapCall_prgen int
@ -3859,9 +3869,17 @@ func wrapCall(n *Node, init *Nodes) *Node {
init.AppendNodes(&n.Ninit) init.AppendNodes(&n.Ninit)
} }
isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER
// origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
origArgs := make([]*Node, n.List.Len())
t := nod(OTFUNC, nil, nil) t := nod(OTFUNC, nil, nil)
for i, arg := range n.List.Slice() { for i, arg := range n.List.Slice() {
s := lookupN("a", i) s := lookupN("a", i)
if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.IsUintptr() && arg.Left.Type.IsUnsafePtr() {
origArgs[i] = arg
arg = arg.Left
n.List.SetIndex(i, arg)
}
t.List.Append(symfield(s, arg.Type)) t.List.Append(symfield(s, arg.Type))
} }
@ -3869,10 +3887,23 @@ func wrapCall(n *Node, init *Nodes) *Node {
sym := lookupN("wrap·", wrapCall_prgen) sym := lookupN("wrap·", wrapCall_prgen)
fn := dclfunc(sym, t) fn := dclfunc(sym, t)
a := nod(n.Op, nil, nil) args := paramNnames(t.Type)
a.List.Set(paramNnames(t.Type)) for i, origArg := range origArgs {
a = typecheck(a, ctxStmt) if origArg == nil {
fn.Nbody.Set1(a) continue
}
arg := nod(origArg.Op, args[i], nil)
arg.Type = origArg.Type
args[i] = arg
}
call := nod(n.Op, nil, nil)
if !isBuiltinCall {
call.Op = OCALL
call.Left = n.Left
call.SetIsDDD(n.IsDDD())
}
call.List.Set(args)
fn.Nbody.Set1(call)
funcbody() funcbody()
@ -3880,12 +3911,12 @@ func wrapCall(n *Node, init *Nodes) *Node {
typecheckslice(fn.Nbody.Slice(), ctxStmt) typecheckslice(fn.Nbody.Slice(), ctxStmt)
xtop = append(xtop, fn) xtop = append(xtop, fn)
a = nod(OCALL, nil, nil) call = nod(OCALL, nil, nil)
a.Left = fn.Func.Nname call.Left = fn.Func.Nname
a.List.Set(n.List.Slice()) call.List.Set(n.List.Slice())
a = typecheck(a, ctxStmt) call = typecheck(call, ctxStmt)
a = walkexpr(a, init) call = walkexpr(call, init)
return a return call
} }
// substArgTypes substitutes the given list of types for // substArgTypes substitutes the given list of types for
@ -3993,10 +4024,14 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
case OADD: case OADD:
walk(n.Left) walk(n.Left)
walk(n.Right) walk(n.Right)
case OSUB, OANDNOT: case OSUB:
walk(n.Left) walk(n.Left)
case OAND:
if n.Implicit() { // was OANDNOT
walk(n.Left)
}
case OCONVNOP: case OCONVNOP:
if n.Left.Type.Etype == TUNSAFEPTR { if n.Left.Type.IsUnsafePtr() {
n.Left = cheapexpr(n.Left, init) n.Left = cheapexpr(n.Left, init)
originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR])) originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR]))
} }

View file

@ -565,6 +565,42 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p = s.Prog(obj.ANOP) p = s.Prog(obj.ANOP)
gc.Patch(pbover, p) gc.Patch(pbover, p)
case ssa.OpPPC64CLRLSLWI:
r := v.Reg()
r1 := v.Args[0].Reg()
shifts := v.AuxInt
p := s.Prog(v.Op.Asm())
// clrlslwi ra,rs,sh,mb will become rlwinm ra,rs,sh,mb-sh,31-n as described in ISA
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftsh(shifts)}
p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftmb(shifts)})
p.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpPPC64CLRLSLDI:
r := v.Reg()
r1 := v.Args[0].Reg()
shifts := v.AuxInt
p := s.Prog(v.Op.Asm())
// clrlsldi ra,rs,sh,mb will become rldic ra,rs,sh,mb-sh
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftsh(shifts)}
p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftmb(shifts)})
p.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = r
// Mask has been set as sh
case ssa.OpPPC64RLDICL:
r := v.Reg()
r1 := v.Args[0].Reg()
shifts := v.AuxInt
p := s.Prog(v.Op.Asm())
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftsh(shifts)}
p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: ssa.GetPPC64Shiftmb(shifts)})
p.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpPPC64ADD, ssa.OpPPC64FADD, ssa.OpPPC64FADDS, ssa.OpPPC64SUB, ssa.OpPPC64FSUB, ssa.OpPPC64FSUBS, case ssa.OpPPC64ADD, ssa.OpPPC64FADD, ssa.OpPPC64FADDS, ssa.OpPPC64SUB, ssa.OpPPC64FSUB, ssa.OpPPC64FSUBS,
ssa.OpPPC64MULLD, ssa.OpPPC64MULLW, ssa.OpPPC64DIVDU, ssa.OpPPC64DIVWU, ssa.OpPPC64MULLD, ssa.OpPPC64MULLW, ssa.OpPPC64DIVDU, ssa.OpPPC64DIVWU,
ssa.OpPPC64SRAD, ssa.OpPPC64SRAW, ssa.OpPPC64SRD, ssa.OpPPC64SRW, ssa.OpPPC64SLD, ssa.OpPPC64SLW, ssa.OpPPC64SRAD, ssa.OpPPC64SRAW, ssa.OpPPC64SRD, ssa.OpPPC64SRW, ssa.OpPPC64SLD, ssa.OpPPC64SLW,
@ -601,6 +637,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpPPC64MADDLD:
r := v.Reg()
r1 := v.Args[0].Reg()
r2 := v.Args[1].Reg()
r3 := v.Args[2].Reg()
// r = r1*r2 ± r3
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = r1
p.Reg = r2
p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r3})
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpPPC64FMADD, ssa.OpPPC64FMADDS, ssa.OpPPC64FMSUB, ssa.OpPPC64FMSUBS: case ssa.OpPPC64FMADD, ssa.OpPPC64FMADDS, ssa.OpPPC64FMSUB, ssa.OpPPC64FMSUBS:
r := v.Reg() r := v.Reg()
r1 := v.Args[0].Reg() r1 := v.Args[0].Reg()
@ -615,23 +665,6 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = r p.To.Reg = r
case ssa.OpPPC64MaskIfNotCarry:
r := v.Reg()
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = ppc64.REGZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpPPC64ADDconstForCarry:
r1 := v.Args[0].Reg()
p := s.Prog(v.Op.Asm())
p.Reg = r1
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REGTMP // Ignored; this is for the carry effect.
case ssa.OpPPC64NEG, ssa.OpPPC64FNEG, ssa.OpPPC64FSQRT, ssa.OpPPC64FSQRTS, ssa.OpPPC64FFLOOR, ssa.OpPPC64FTRUNC, ssa.OpPPC64FCEIL, case ssa.OpPPC64NEG, ssa.OpPPC64FNEG, ssa.OpPPC64FSQRT, ssa.OpPPC64FSQRTS, ssa.OpPPC64FFLOOR, ssa.OpPPC64FTRUNC, ssa.OpPPC64FCEIL,
ssa.OpPPC64FCTIDZ, ssa.OpPPC64FCTIWZ, ssa.OpPPC64FCFID, ssa.OpPPC64FCFIDS, ssa.OpPPC64FRSP, ssa.OpPPC64CNTLZD, ssa.OpPPC64CNTLZW, ssa.OpPPC64FCTIDZ, ssa.OpPPC64FCTIWZ, ssa.OpPPC64FCFID, ssa.OpPPC64FCFIDS, ssa.OpPPC64FRSP, ssa.OpPPC64CNTLZD, ssa.OpPPC64CNTLZW,
ssa.OpPPC64POPCNTD, ssa.OpPPC64POPCNTW, ssa.OpPPC64POPCNTB, ssa.OpPPC64MFVSRD, ssa.OpPPC64MTVSRD, ssa.OpPPC64FABS, ssa.OpPPC64FNABS, ssa.OpPPC64POPCNTD, ssa.OpPPC64POPCNTW, ssa.OpPPC64POPCNTB, ssa.OpPPC64MFVSRD, ssa.OpPPC64MTVSRD, ssa.OpPPC64FABS, ssa.OpPPC64FNABS,
@ -652,6 +685,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpPPC64SUBFCconst:
p := s.Prog(v.Op.Asm())
p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt})
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpPPC64ANDCCconst: case ssa.OpPPC64ANDCCconst:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.Reg = v.Args[0].Reg() p.Reg = v.Args[0].Reg()
@ -1788,7 +1829,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("Pseudo-op should not make it to codegen: %s ###\n", v.LongString()) v.Fatalf("Pseudo-op should not make it to codegen: %s ###\n", v.LongString())
case ssa.OpPPC64InvertFlags: case ssa.OpPPC64InvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT, ssa.OpPPC64FlagCarrySet, ssa.OpPPC64FlagCarryClear: case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString()) v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
case ssa.OpClobber: case ssa.OpClobber:
// TODO: implement for clobberdead experiment. Nop is ok for now. // TODO: implement for clobberdead experiment. Nop is ok for now.

View file

@ -338,8 +338,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
n.To.Reg = dividend n.To.Reg = dividend
} }
j.To.Val = n j.To.SetTarget(n)
j2.To.Val = s.Pc() j2.To.SetTarget(s.Pc())
} }
case ssa.OpS390XADDconst, ssa.OpS390XADDWconst: case ssa.OpS390XADDconst, ssa.OpS390XADDWconst:
opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt) opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt)

View file

@ -7,12 +7,14 @@ package ssa
// addressingModes combines address calculations into memory operations // addressingModes combines address calculations into memory operations
// that can perform complicated addressing modes. // that can perform complicated addressing modes.
func addressingModes(f *Func) { func addressingModes(f *Func) {
isInImmediateRange := is32Bit
switch f.Config.arch { switch f.Config.arch {
default: default:
// Most architectures can't do this. // Most architectures can't do this.
return return
case "amd64", "386": case "amd64", "386":
// TODO: s390x? case "s390x":
isInImmediateRange = is20Bit
} }
var tmp []*Value var tmp []*Value
@ -40,7 +42,7 @@ func addressingModes(f *Func) {
switch [2]auxType{opcodeTable[v.Op].auxType, opcodeTable[p.Op].auxType} { switch [2]auxType{opcodeTable[v.Op].auxType, opcodeTable[p.Op].auxType} {
case [2]auxType{auxSymOff, auxInt32}: case [2]auxType{auxSymOff, auxInt32}:
// TODO: introduce auxSymOff32 // TODO: introduce auxSymOff32
if !is32Bit(v.AuxInt + p.AuxInt) { if !isInImmediateRange(v.AuxInt + p.AuxInt) {
continue continue
} }
v.AuxInt += p.AuxInt v.AuxInt += p.AuxInt
@ -48,7 +50,7 @@ func addressingModes(f *Func) {
if v.Aux != nil && p.Aux != nil { if v.Aux != nil && p.Aux != nil {
continue continue
} }
if !is32Bit(v.AuxInt + p.AuxInt) { if !isInImmediateRange(v.AuxInt + p.AuxInt) {
continue continue
} }
if p.Aux != nil { if p.Aux != nil {
@ -321,6 +323,23 @@ var combine = map[[2]Op]Op{
[2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ1}: OpAMD64XORQconstmodifyidx1, [2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ1}: OpAMD64XORQconstmodifyidx1,
[2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ8}: OpAMD64XORQconstmodifyidx8, [2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ8}: OpAMD64XORQconstmodifyidx8,
[2]Op{OpAMD64ADDSSload, OpAMD64LEAQ1}: OpAMD64ADDSSloadidx1,
[2]Op{OpAMD64ADDSSload, OpAMD64LEAQ4}: OpAMD64ADDSSloadidx4,
[2]Op{OpAMD64ADDSDload, OpAMD64LEAQ1}: OpAMD64ADDSDloadidx1,
[2]Op{OpAMD64ADDSDload, OpAMD64LEAQ8}: OpAMD64ADDSDloadidx8,
[2]Op{OpAMD64SUBSSload, OpAMD64LEAQ1}: OpAMD64SUBSSloadidx1,
[2]Op{OpAMD64SUBSSload, OpAMD64LEAQ4}: OpAMD64SUBSSloadidx4,
[2]Op{OpAMD64SUBSDload, OpAMD64LEAQ1}: OpAMD64SUBSDloadidx1,
[2]Op{OpAMD64SUBSDload, OpAMD64LEAQ8}: OpAMD64SUBSDloadidx8,
[2]Op{OpAMD64MULSSload, OpAMD64LEAQ1}: OpAMD64MULSSloadidx1,
[2]Op{OpAMD64MULSSload, OpAMD64LEAQ4}: OpAMD64MULSSloadidx4,
[2]Op{OpAMD64MULSDload, OpAMD64LEAQ1}: OpAMD64MULSDloadidx1,
[2]Op{OpAMD64MULSDload, OpAMD64LEAQ8}: OpAMD64MULSDloadidx8,
[2]Op{OpAMD64DIVSSload, OpAMD64LEAQ1}: OpAMD64DIVSSloadidx1,
[2]Op{OpAMD64DIVSSload, OpAMD64LEAQ4}: OpAMD64DIVSSloadidx4,
[2]Op{OpAMD64DIVSDload, OpAMD64LEAQ1}: OpAMD64DIVSDloadidx1,
[2]Op{OpAMD64DIVSDload, OpAMD64LEAQ8}: OpAMD64DIVSDloadidx8,
// 386 // 386
[2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1, [2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1,
[2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1, [2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1,
@ -381,4 +400,61 @@ var combine = map[[2]Op]Op{
[2]Op{Op386ANDLconstmodify, Op386LEAL4}: Op386ANDLconstmodifyidx4, [2]Op{Op386ANDLconstmodify, Op386LEAL4}: Op386ANDLconstmodifyidx4,
[2]Op{Op386ORLconstmodify, Op386LEAL4}: Op386ORLconstmodifyidx4, [2]Op{Op386ORLconstmodify, Op386LEAL4}: Op386ORLconstmodifyidx4,
[2]Op{Op386XORLconstmodify, Op386LEAL4}: Op386XORLconstmodifyidx4, [2]Op{Op386XORLconstmodify, Op386LEAL4}: Op386XORLconstmodifyidx4,
// s390x
[2]Op{OpS390XMOVDload, OpS390XADD}: OpS390XMOVDloadidx,
[2]Op{OpS390XMOVWload, OpS390XADD}: OpS390XMOVWloadidx,
[2]Op{OpS390XMOVHload, OpS390XADD}: OpS390XMOVHloadidx,
[2]Op{OpS390XMOVBload, OpS390XADD}: OpS390XMOVBloadidx,
[2]Op{OpS390XMOVWZload, OpS390XADD}: OpS390XMOVWZloadidx,
[2]Op{OpS390XMOVHZload, OpS390XADD}: OpS390XMOVHZloadidx,
[2]Op{OpS390XMOVBZload, OpS390XADD}: OpS390XMOVBZloadidx,
[2]Op{OpS390XMOVDBRload, OpS390XADD}: OpS390XMOVDBRloadidx,
[2]Op{OpS390XMOVWBRload, OpS390XADD}: OpS390XMOVWBRloadidx,
[2]Op{OpS390XMOVHBRload, OpS390XADD}: OpS390XMOVHBRloadidx,
[2]Op{OpS390XFMOVDload, OpS390XADD}: OpS390XFMOVDloadidx,
[2]Op{OpS390XFMOVSload, OpS390XADD}: OpS390XFMOVSloadidx,
[2]Op{OpS390XMOVDstore, OpS390XADD}: OpS390XMOVDstoreidx,
[2]Op{OpS390XMOVWstore, OpS390XADD}: OpS390XMOVWstoreidx,
[2]Op{OpS390XMOVHstore, OpS390XADD}: OpS390XMOVHstoreidx,
[2]Op{OpS390XMOVBstore, OpS390XADD}: OpS390XMOVBstoreidx,
[2]Op{OpS390XMOVDBRstore, OpS390XADD}: OpS390XMOVDBRstoreidx,
[2]Op{OpS390XMOVWBRstore, OpS390XADD}: OpS390XMOVWBRstoreidx,
[2]Op{OpS390XMOVHBRstore, OpS390XADD}: OpS390XMOVHBRstoreidx,
[2]Op{OpS390XFMOVDstore, OpS390XADD}: OpS390XFMOVDstoreidx,
[2]Op{OpS390XFMOVSstore, OpS390XADD}: OpS390XFMOVSstoreidx,
[2]Op{OpS390XMOVDload, OpS390XMOVDaddridx}: OpS390XMOVDloadidx,
[2]Op{OpS390XMOVWload, OpS390XMOVDaddridx}: OpS390XMOVWloadidx,
[2]Op{OpS390XMOVHload, OpS390XMOVDaddridx}: OpS390XMOVHloadidx,
[2]Op{OpS390XMOVBload, OpS390XMOVDaddridx}: OpS390XMOVBloadidx,
[2]Op{OpS390XMOVWZload, OpS390XMOVDaddridx}: OpS390XMOVWZloadidx,
[2]Op{OpS390XMOVHZload, OpS390XMOVDaddridx}: OpS390XMOVHZloadidx,
[2]Op{OpS390XMOVBZload, OpS390XMOVDaddridx}: OpS390XMOVBZloadidx,
[2]Op{OpS390XMOVDBRload, OpS390XMOVDaddridx}: OpS390XMOVDBRloadidx,
[2]Op{OpS390XMOVWBRload, OpS390XMOVDaddridx}: OpS390XMOVWBRloadidx,
[2]Op{OpS390XMOVHBRload, OpS390XMOVDaddridx}: OpS390XMOVHBRloadidx,
[2]Op{OpS390XFMOVDload, OpS390XMOVDaddridx}: OpS390XFMOVDloadidx,
[2]Op{OpS390XFMOVSload, OpS390XMOVDaddridx}: OpS390XFMOVSloadidx,
[2]Op{OpS390XMOVDstore, OpS390XMOVDaddridx}: OpS390XMOVDstoreidx,
[2]Op{OpS390XMOVWstore, OpS390XMOVDaddridx}: OpS390XMOVWstoreidx,
[2]Op{OpS390XMOVHstore, OpS390XMOVDaddridx}: OpS390XMOVHstoreidx,
[2]Op{OpS390XMOVBstore, OpS390XMOVDaddridx}: OpS390XMOVBstoreidx,
[2]Op{OpS390XMOVDBRstore, OpS390XMOVDaddridx}: OpS390XMOVDBRstoreidx,
[2]Op{OpS390XMOVWBRstore, OpS390XMOVDaddridx}: OpS390XMOVWBRstoreidx,
[2]Op{OpS390XMOVHBRstore, OpS390XMOVDaddridx}: OpS390XMOVHBRstoreidx,
[2]Op{OpS390XFMOVDstore, OpS390XMOVDaddridx}: OpS390XFMOVDstoreidx,
[2]Op{OpS390XFMOVSstore, OpS390XMOVDaddridx}: OpS390XFMOVSstoreidx,
} }

View file

@ -165,16 +165,28 @@ func checkFunc(f *Func) {
f.Fatalf("value %v has Aux type %T, want string", v, v.Aux) f.Fatalf("value %v has Aux type %T, want string", v, v.Aux)
} }
canHaveAux = true canHaveAux = true
case auxCallOff:
canHaveAuxInt = true
fallthrough
case auxCall:
if ac, ok := v.Aux.(*AuxCall); ok {
if v.Op == OpStaticCall && ac.Fn == nil {
f.Fatalf("value %v has *AuxCall with nil Fn", v)
}
} else {
f.Fatalf("value %v has Aux type %T, want *AuxCall", v, v.Aux)
}
canHaveAux = true
case auxSym, auxTyp: case auxSym, auxTyp:
canHaveAux = true canHaveAux = true
case auxSymOff, auxSymValAndOff, auxTypSize: case auxSymOff, auxSymValAndOff, auxTypSize:
canHaveAuxInt = true canHaveAuxInt = true
canHaveAux = true canHaveAux = true
case auxCCop: case auxCCop:
if _, ok := v.Aux.(Op); !ok { if opcodeTable[Op(v.AuxInt)].name == "OpInvalid" {
f.Fatalf("bad type %T for CCop in %v", v.Aux, v) f.Fatalf("value %v has an AuxInt value that is a valid opcode", v)
} }
canHaveAux = true canHaveAuxInt = true
case auxS390XCCMask: case auxS390XCCMask:
if _, ok := v.Aux.(s390x.CCMask); !ok { if _, ok := v.Aux.(s390x.CCMask); !ok {
f.Fatalf("bad type %T for S390XCCMask in %v", v.Aux, v) f.Fatalf("bad type %T for S390XCCMask in %v", v.Aux, v)
@ -257,6 +269,38 @@ func checkFunc(f *Func) {
f.Fatalf("bad %s type: want uintptr, have %s", f.Fatalf("bad %s type: want uintptr, have %s",
v.Op, v.Type.String()) v.Op, v.Type.String())
} }
case OpStringLen:
if v.Type != c.Types.Int {
f.Fatalf("bad %s type: want int, have %s",
v.Op, v.Type.String())
}
case OpLoad:
if !v.Args[1].Type.IsMemory() {
f.Fatalf("bad arg 1 type to %s: want mem, have %s",
v.Op, v.Args[1].Type.String())
}
case OpStore:
if !v.Type.IsMemory() {
f.Fatalf("bad %s type: want mem, have %s",
v.Op, v.Type.String())
}
if !v.Args[2].Type.IsMemory() {
f.Fatalf("bad arg 2 type to %s: want mem, have %s",
v.Op, v.Args[2].Type.String())
}
case OpCondSelect:
if !v.Args[2].Type.IsBoolean() {
f.Fatalf("bad arg 2 type to %s: want boolean, have %s",
v.Op, v.Args[2].Type.String())
}
case OpAddPtr:
if !v.Args[0].Type.IsPtrShaped() && v.Args[0].Type != c.Types.Uintptr {
f.Fatalf("bad arg 0 type to %s: want ptr, have %s", v.Op, v.Args[0].LongString())
}
if !v.Args[1].Type.IsInteger() {
f.Fatalf("bad arg 1 type to %s: want integer, have %s", v.Op, v.Args[1].LongString())
}
} }
// TODO: check for cycles in values // TODO: check for cycles in values

View file

@ -160,15 +160,12 @@ func Compile(f *Func) {
phaseName = "" phaseName = ""
} }
// TODO: should be a config field
var dumpFileSeq int
// dumpFile creates a file from the phase name and function name // dumpFile creates a file from the phase name and function name
// Dumping is done to files to avoid buffering huge strings before // Dumping is done to files to avoid buffering huge strings before
// output. // output.
func (f *Func) dumpFile(phaseName string) { func (f *Func) dumpFile(phaseName string) {
dumpFileSeq++ f.dumpFileSeq++
fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, dumpFileSeq, phaseName) fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName)
fname = strings.Replace(fname, " ", "_", -1) fname = strings.Replace(fname, " ", "_", -1)
fname = strings.Replace(fname, "/", "_", -1) fname = strings.Replace(fname, "/", "_", -1)
fname = strings.Replace(fname, ":", "_", -1) fname = strings.Replace(fname, ":", "_", -1)
@ -436,6 +433,7 @@ var passes = [...]pass{
{name: "early fuse", fn: fuseEarly}, {name: "early fuse", fn: fuseEarly},
{name: "decompose builtin", fn: decomposeBuiltIn, required: true}, {name: "decompose builtin", fn: decomposeBuiltIn, required: true},
{name: "softfloat", fn: softfloat, required: true}, {name: "softfloat", fn: softfloat, required: true},
{name: "expand calls", fn:expandCalls, required: true},
{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules {name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
{name: "dead auto elim", fn: elimDeadAutosGeneric}, {name: "dead auto elim", fn: elimDeadAutosGeneric},
{name: "generic deadcode", fn: deadcode, required: true}, // remove dead stores, which otherwise mess up store chain {name: "generic deadcode", fn: deadcode, required: true}, // remove dead stores, which otherwise mess up store chain

View file

@ -173,6 +173,9 @@ type Frontend interface {
// SetWBPos indicates that a write barrier has been inserted // SetWBPos indicates that a write barrier has been inserted
// in this function at position pos. // in this function at position pos.
SetWBPos(pos src.XPos) SetWBPos(pos src.XPos)
// MyImportPath provides the import name (roughly, the package) for the function being compiled.
MyImportPath() string
} }
// interface used to hold a *gc.Node (a stack variable). // interface used to hold a *gc.Node (a stack variable).
@ -245,7 +248,7 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.FPReg = framepointerRegARM64 c.FPReg = framepointerRegARM64
c.LinkReg = linkRegARM64 c.LinkReg = linkRegARM64
c.hasGReg = true c.hasGReg = true
c.noDuffDevice = objabi.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend c.noDuffDevice = objabi.GOOS == "darwin" || objabi.GOOS == "ios" // darwin linker cannot handle BR26 reloc with non-zero addend
case "ppc64": case "ppc64":
c.BigEndian = true c.BigEndian = true
fallthrough fallthrough

View file

@ -1,6 +1,7 @@
// Copyright 2017 The Go Authors. All rights reserved. // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package ssa package ssa
import ( import (

View file

@ -23,9 +23,11 @@ func decomposeBuiltIn(f *Func) {
} }
// Decompose other values // Decompose other values
applyRewrite(f, rewriteBlockdec, rewriteValuedec) // Note: deadcode is false because we need to keep the original
// values around so the name component resolution below can still work.
applyRewrite(f, rewriteBlockdec, rewriteValuedec, leaveDeadValues)
if f.Config.RegSize == 4 { if f.Config.RegSize == 4 {
applyRewrite(f, rewriteBlockdec64, rewriteValuedec64) applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, leaveDeadValues)
} }
// Split up named values into their components. // Split up named values into their components.
@ -139,7 +141,7 @@ func decomposeStringPhi(v *Value) {
func decomposeSlicePhi(v *Value) { func decomposeSlicePhi(v *Value) {
types := &v.Block.Func.Config.Types types := &v.Block.Func.Config.Types
ptrType := types.BytePtr ptrType := v.Type.Elem().PtrTo()
lenType := types.Int lenType := types.Int
ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType) ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType)
@ -215,7 +217,7 @@ func decomposeInterfacePhi(v *Value) {
} }
func decomposeArgs(f *Func) { func decomposeArgs(f *Func) {
applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs) applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs, removeDeadValues)
} }
func decomposeUser(f *Func) { func decomposeUser(f *Func) {

View file

@ -0,0 +1,100 @@
// Copyright 2020 The Go Authors. 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 "cmd/compile/internal/types"
// expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form
// that is more oriented to a platform's ABI. The SelectN operations that extract results are also rewritten into
// more appropriate forms.
func expandCalls(f *Func) {
canSSAType := f.fe.CanSSA
sp, _ := f.spSb()
// Calls that need lowering have some number of inputs, including a memory input,
// and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
// With the current ABI those inputs need to be converted into stores to memory,
// rethreading the call's memory input to the first, and the new call now receiving the last.
// With the current ABI, the outputs need to be converted to loads, which will all use the call's
// memory output as their input.
// Step 1: find all references to calls as values and rewrite those.
for _, b := range f.Blocks {
for _, v := range b.Values {
switch v.Op {
case OpSelectN:
call := v.Args[0]
aux := call.Aux.(*AuxCall)
which := v.AuxInt
t := v.Type
if which == aux.NResults() { // mem is after the results.
// rewrite v as a Copy of call -- the replacement call will produce a mem.
v.copyOf(call)
} else {
pt := types.NewPtr(t)
if canSSAType(t) {
off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp)
v.reset(OpLoad)
v.SetArgs2(off, call)
} else {
panic("Should not have non-SSA-able OpSelectN")
}
}
v.Type = t // not right for the mem operand yet, but will be when call is rewritten.
case OpSelectNAddr:
call := v.Args[0]
which := v.AuxInt
aux := call.Aux.(*AuxCall)
pt := v.Type
off := f.ConstOffPtrSP(pt, aux.OffsetOfResult(which), sp)
v.copyOf(off)
}
}
}
// Step 2: rewrite the calls
for _, b := range f.Blocks {
for _, v := range b.Values {
switch v.Op {
case OpStaticLECall:
// Thread the stores on the memory arg
m0 := v.Args[len(v.Args)-1]
mem := m0
pos := v.Pos.WithNotStmt()
aux := v.Aux.(*AuxCall)
auxInt := v.AuxInt
for i, a := range v.Args {
if a == m0 {
break
}
if a.Op == OpDereference {
// "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
src := a.Args[0]
dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(int64(i)), sp)
a.reset(OpMove)
a.Pos = pos
a.Type = types.TypeMem
a.Aux = aux.TypeOfArg(int64(i))
a.AuxInt = aux.SizeOfArg(int64(i))
a.SetArgs3(dst, src, mem)
mem = a
} else {
// Add a new store.
t := aux.TypeOfArg(int64(i))
dst := f.ConstOffPtrSP(types.NewPtr(t), aux.OffsetOfArg(int64(i)), sp)
mem = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, a, mem)
}
}
v.reset(OpStaticCall)
v.Type = types.TypeMem
v.Aux = aux
v.AuxInt = auxInt
v.SetArgs1(mem)
}
}
}
}

View file

@ -146,6 +146,10 @@ func (d DummyFrontend) Fatalf(_ src.XPos, msg string, args ...interface{}) { d.t
func (d DummyFrontend) Warnl(_ src.XPos, msg string, args ...interface{}) { d.t.Logf(msg, args...) } func (d DummyFrontend) Warnl(_ src.XPos, msg string, args ...interface{}) { d.t.Logf(msg, args...) }
func (d DummyFrontend) Debug_checknil() bool { return false } func (d DummyFrontend) Debug_checknil() bool { return false }
func (d DummyFrontend) MyImportPath() string {
return "my/import/path"
}
var dummyTypes Types var dummyTypes Types
func init() { func init() {

View file

@ -33,15 +33,8 @@ type Func struct {
Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID) Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID)
Entry *Block // the entry basic block Entry *Block // the entry basic block
// If we are using open-coded defers, this is the first call to a deferred bid idAlloc // block ID allocator
// function in the final defer exit sequence that we generated. This call vid idAlloc // value ID allocator
// should be after all defer statements, and will have all args, etc. of
// all defer calls as live. The liveness info of this call will be used
// for the deferreturn/ret segment generated for functions with open-coded
// defers.
LastDeferExit *Value
bid idAlloc // block ID allocator
vid idAlloc // value ID allocator
// Given an environment variable used for debug hash match, // Given an environment variable used for debug hash match,
// what file (if any) receives the yes/no logging? // what file (if any) receives the yes/no logging?
@ -51,9 +44,10 @@ type Func struct {
PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false. PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false.
ruleMatches map[string]int // number of times countRule was called during compilation for any given string ruleMatches map[string]int // number of times countRule was called during compilation for any given string
scheduled bool // Values in Blocks are in final order scheduled bool // Values in Blocks are in final order
laidout bool // Blocks are ordered laidout bool // Blocks are ordered
NoSplit bool // true if function is marked as nosplit. Used by schedule check pass. NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
dumpFileSeq uint8 // the sequence numbers of dump file. (%s_%02d__%s.dump", funcname, dumpFileSeq, phaseName)
// when register allocation is done, maps value ids to locations // when register allocation is done, maps value ids to locations
RegAlloc []Location RegAlloc []Location
@ -263,6 +257,49 @@ func (f *Func) LogStat(key string, args ...interface{}) {
f.Warnl(f.Entry.Pos, "\t%s\t%s%s\t%s", n, key, value, f.Name) f.Warnl(f.Entry.Pos, "\t%s\t%s%s\t%s", n, key, value, f.Name)
} }
// unCacheLine removes v from f's constant cache "line" for aux,
// resets v.InCache when it is found (and removed),
// and returns whether v was found in that line.
func (f *Func) unCacheLine(v *Value, aux int64) bool {
vv := f.constants[aux]
for i, cv := range vv {
if v == cv {
vv[i] = vv[len(vv)-1]
vv[len(vv)-1] = nil
f.constants[aux] = vv[0 : len(vv)-1]
v.InCache = false
return true
}
}
return false
}
// unCache removes v from f's constant cache.
func (f *Func) unCache(v *Value) {
if v.InCache {
aux := v.AuxInt
if f.unCacheLine(v, aux) {
return
}
if aux == 0 {
switch v.Op {
case OpConstNil:
aux = constNilMagic
case OpConstSlice:
aux = constSliceMagic
case OpConstString:
aux = constEmptyStringMagic
case OpConstInterface:
aux = constInterfaceMagic
}
if aux != 0 && f.unCacheLine(v, aux) {
return
}
}
f.Fatalf("unCached value %s not found in cache, auxInt=0x%x, adjusted aux=0x%x", v.LongString(), v.AuxInt, aux)
}
}
// freeValue frees a value. It must no longer be referenced or have any args. // freeValue frees a value. It must no longer be referenced or have any args.
func (f *Func) freeValue(v *Value) { func (f *Func) freeValue(v *Value) {
if v.Block == nil { if v.Block == nil {
@ -276,19 +313,8 @@ func (f *Func) freeValue(v *Value) {
} }
// Clear everything but ID (which we reuse). // Clear everything but ID (which we reuse).
id := v.ID id := v.ID
if v.InCache {
// Values with zero arguments and OpOffPtr values might be cached, so remove them there. f.unCache(v)
nArgs := opcodeTable[v.Op].argLen
if nArgs == 0 || v.Op == OpOffPtr {
vv := f.constants[v.AuxInt]
for i, cv := range vv {
if v == cv {
vv[i] = vv[len(vv)-1]
vv[len(vv)-1] = nil
f.constants[v.AuxInt] = vv[0 : len(vv)-1]
break
}
}
} }
*v = Value{} *v = Value{}
v.ID = id v.ID = id
@ -554,6 +580,7 @@ func (f *Func) constVal(op Op, t *types.Type, c int64, setAuxInt bool) *Value {
v = f.Entry.NewValue0(src.NoXPos, op, t) v = f.Entry.NewValue0(src.NoXPos, op, t)
} }
f.constants[c] = append(vv, v) f.constants[c] = append(vv, v)
v.InCache = true
return v return v
} }
@ -684,7 +711,8 @@ func (f *Func) invalidateCFG() {
// GSHS_LOGFILE // GSHS_LOGFILE
// or standard out if that is empty or there is an error // or standard out if that is empty or there is an error
// opening the file. // opening the file.
func (f *Func) DebugHashMatch(evname, name string) bool { func (f *Func) DebugHashMatch(evname string) bool {
name := f.fe.MyImportPath() + "." + f.Name
evhash := os.Getenv(evname) evhash := os.Getenv(evname)
switch evhash { switch evhash {
case "": case "":
@ -733,7 +761,7 @@ func (f *Func) logDebugHashMatch(evname, name string) {
file = os.Stdout file = os.Stdout
if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" { if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" {
var err error var err error
file, err = os.Create(tmpfile) file, err = os.OpenFile(tmpfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil { if err != nil {
f.Fatalf("could not open hash-testing logfile %s", tmpfile) f.Fatalf("could not open hash-testing logfile %s", tmpfile)
} }
@ -747,3 +775,25 @@ func (f *Func) logDebugHashMatch(evname, name string) {
func DebugNameMatch(evname, name string) bool { func DebugNameMatch(evname, name string) bool {
return os.Getenv(evname) == name return os.Getenv(evname) == name
} }
func (f *Func) spSb() (sp, sb *Value) {
initpos := f.Entry.Pos
for _, v := range f.Entry.Values {
if v.Op == OpSB {
sb = v
}
if v.Op == OpSP {
sp = v
}
if sb != nil && sp != nil {
break
}
}
if sb == nil {
sb = f.Entry.NewValue0(initpos, OpSB, f.Config.Types.Uintptr)
}
if sp == nil {
sp = f.Entry.NewValue0(initpos, OpSP, f.Config.Types.Uintptr)
}
return
}

View file

@ -38,6 +38,7 @@ package ssa
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src" "cmd/internal/src"
"fmt" "fmt"
"reflect" "reflect"
@ -140,6 +141,12 @@ var emptyPass pass = pass{
name: "empty pass", name: "empty pass",
} }
// AuxCallLSym returns an AuxCall initialized with an LSym that should pass "check"
// as the Aux of a static call.
func AuxCallLSym(name string) *AuxCall {
return &AuxCall{Fn: &obj.LSym{}}
}
// Fun takes the name of an entry bloc and a series of Bloc calls, and // Fun takes the name of an entry bloc and a series of Bloc calls, and
// returns a fun containing the composed Func. entry must be a name // returns a fun containing the composed Func. entry must be a name
// supplied to one of the Bloc functions. Each of the bloc names and // supplied to one of the Bloc functions. Each of the bloc names and

View file

@ -142,10 +142,10 @@ func TestFuseSideEffects(t *testing.T) {
Valu("b", OpArg, c.config.Types.Bool, 0, nil), Valu("b", OpArg, c.config.Types.Bool, 0, nil),
If("b", "then", "else")), If("b", "then", "else")),
Bloc("then", Bloc("then",
Valu("call1", OpStaticCall, types.TypeMem, 0, nil, "mem"), Valu("call1", OpStaticCall, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
Goto("empty")), Goto("empty")),
Bloc("else", Bloc("else",
Valu("call2", OpStaticCall, types.TypeMem, 0, nil, "mem"), Valu("call2", OpStaticCall, types.TypeMem, 0, AuxCallLSym("_"), "mem"),
Goto("empty")), Goto("empty")),
Bloc("empty", Bloc("empty",
Goto("loop")), Goto("loop")),

View file

@ -463,9 +463,9 @@ func init() {
faultOnNilArg0: true, faultOnNilArg0: true,
}, },
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// arg0 = destination pointer // arg0 = destination pointer
// arg1 = source pointer // arg1 = source pointer

File diff suppressed because it is too large Load diff

View file

@ -149,14 +149,15 @@ func init() {
gpstorexchg = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: []regMask{gp}} gpstorexchg = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: []regMask{gp}}
cmpxchg = regInfo{inputs: []regMask{gp, ax, gp, 0}, outputs: []regMask{gp, 0}, clobbers: ax} cmpxchg = regInfo{inputs: []regMask{gp, ax, gp, 0}, outputs: []regMask{gp, 0}, clobbers: ax}
fp01 = regInfo{inputs: nil, outputs: fponly} fp01 = regInfo{inputs: nil, outputs: fponly}
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly} fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly} fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly} fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly}
fpgp = regInfo{inputs: fponly, outputs: gponly} fp21loadidx = regInfo{inputs: []regMask{fp, gpspsb, gpspsb, 0}, outputs: fponly}
gpfp = regInfo{inputs: gponly, outputs: fponly} fpgp = regInfo{inputs: fponly, outputs: gponly}
fp11 = regInfo{inputs: fponly, outputs: fponly} gpfp = regInfo{inputs: gponly, outputs: fponly}
fp2flags = regInfo{inputs: []regMask{fp, fp}} fp11 = regInfo{inputs: fponly, outputs: fponly}
fp2flags = regInfo{inputs: []regMask{fp, fp}}
fpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: fponly} fpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: fponly}
fploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: fponly} fploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: fponly}
@ -201,6 +202,23 @@ func init() {
{name: "DIVSSload", argLength: 3, reg: fp21load, asm: "DIVSS", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem {name: "DIVSSload", argLength: 3, reg: fp21load, asm: "DIVSS", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{name: "DIVSDload", argLength: 3, reg: fp21load, asm: "DIVSD", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem {name: "DIVSDload", argLength: 3, reg: fp21load, asm: "DIVSD", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{name: "ADDSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "ADDSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 + tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "ADDSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "ADDSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 + tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
{name: "ADDSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "ADDSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 + tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "ADDSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "ADDSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 + tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
{name: "SUBSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "SUBSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 - tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "SUBSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "SUBSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 - tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
{name: "SUBSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "SUBSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 - tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "SUBSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "SUBSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 - tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
{name: "MULSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "MULSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 * tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "MULSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "MULSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 * tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
{name: "MULSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "MULSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 * tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "MULSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "MULSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 * tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
{name: "DIVSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "DIVSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "DIVSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "DIVSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem
{name: "DIVSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "DIVSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem
{name: "DIVSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "DIVSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem
// binary ops // binary ops
{name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true, clobberFlags: true}, // arg0 + arg1 {name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true, clobberFlags: true}, // arg0 + arg1
{name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true, clobberFlags: true}, // arg0 + arg1 {name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true, clobberFlags: true}, // arg0 + arg1
@ -730,6 +748,7 @@ func init() {
clobbers: buildReg("DI"), clobbers: buildReg("DI"),
}, },
faultOnNilArg0: true, faultOnNilArg0: true,
unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
}, },
{name: "MOVOconst", reg: regInfo{nil, 0, []regMask{fp}}, typ: "Int128", aux: "Int128", rematerializeable: true}, {name: "MOVOconst", reg: regInfo{nil, 0, []regMask{fp}}, typ: "Int128", aux: "Int128", rematerializeable: true},
@ -748,9 +767,9 @@ func init() {
faultOnNilArg0: true, faultOnNilArg0: true,
}, },
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// arg0 = destination pointer // arg0 = destination pointer
// arg1 = source pointer // arg1 = source pointer
@ -768,6 +787,7 @@ func init() {
clobberFlags: true, clobberFlags: true,
faultOnNilArg0: true, faultOnNilArg0: true,
faultOnNilArg1: true, faultOnNilArg1: true,
unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
}, },
// arg0 = destination pointer // arg0 = destination pointer

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -467,13 +467,13 @@ func init() {
// conditional instructions; auxint is // conditional instructions; auxint is
// one of the arm64 comparison pseudo-ops (LessThan, LessThanU, etc.) // one of the arm64 comparison pseudo-ops (LessThan, LessThanU, etc.)
{name: "CSEL", argLength: 3, reg: gp2flags1, asm: "CSEL", aux: "CCop"}, // aux(flags) ? arg0 : arg1 {name: "CSEL", argLength: 3, reg: gp2flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : arg1
{name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // aux(flags) ? arg0 : 0 {name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : 0
// function calls // function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// pseudo-ops // pseudo-ops
{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem.
@ -507,6 +507,7 @@ func init() {
clobbers: buildReg("R20 R30"), clobbers: buildReg("R20 R30"),
}, },
faultOnNilArg0: true, faultOnNilArg0: true,
unsafePoint: true, // FP maintenance around DUFFZERO can be clobbered by interrupts
}, },
// large zeroing // large zeroing
@ -547,6 +548,7 @@ func init() {
}, },
faultOnNilArg0: true, faultOnNilArg0: true,
faultOnNilArg1: true, faultOnNilArg1: true,
unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
}, },
// large move // large move

View file

@ -428,9 +428,9 @@ func init() {
{name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"}, // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags {name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"}, // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags
// function calls // function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// pseudo-ops // pseudo-ops
{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem.

View file

@ -11,8 +11,8 @@
(Mul(64|32|16|8) x y) => (Select1 (MULVU x y)) (Mul(64|32|16|8) x y) => (Select1 (MULVU x y))
(Mul(32|64)F ...) => (MUL(F|D) ...) (Mul(32|64)F ...) => (MUL(F|D) ...)
(Mul64uhilo ...) => (MULVU ...) (Mul64uhilo ...) => (MULVU ...)
(Select0 (Mul64uover x y)) -> (Select1 <typ.UInt64> (MULVU x y)) (Select0 (Mul64uover x y)) => (Select1 <typ.UInt64> (MULVU x y))
(Select1 (Mul64uover x y)) -> (SGTU <typ.Bool> (Select0 <typ.UInt64> (MULVU x y)) (MOVVconst <typ.UInt64> [0])) (Select1 (Mul64uover x y)) => (SGTU <typ.Bool> (Select0 <typ.UInt64> (MULVU x y)) (MOVVconst <typ.UInt64> [0]))
(Hmul64 x y) => (Select0 (MULV x y)) (Hmul64 x y) => (Select0 (MULV x y))
(Hmul64u x y) => (Select0 (MULVU x y)) (Hmul64u x y) => (Select0 (MULVU x y))
@ -38,8 +38,8 @@
(Mod8 x y) => (Select0 (DIVV (SignExt8to64 x) (SignExt8to64 y))) (Mod8 x y) => (Select0 (DIVV (SignExt8to64 x) (SignExt8to64 y)))
(Mod8u x y) => (Select0 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y))) (Mod8u x y) => (Select0 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y)))
// (x + y) / 2 with x>=y -> (x - y) / 2 + y // (x + y) / 2 with x>=y => (x - y) / 2 + y
(Avg64u <t> x y) -> (ADDV (SRLVconst <t> (SUBV <t> x y) [1]) y) (Avg64u <t> x y) => (ADDV (SRLVconst <t> (SUBV <t> x y) [1]) y)
(And(64|32|16|8) ...) => (AND ...) (And(64|32|16|8) ...) => (AND ...)
(Or(64|32|16|8) ...) => (OR ...) (Or(64|32|16|8) ...) => (OR ...)
@ -130,10 +130,10 @@
(Not x) => (XORconst [1] x) (Not x) => (XORconst [1] x)
// constants // constants
(Const(64|32|16|8) ...) -> (MOVVconst ...) (Const(64|32|16|8) [val]) => (MOVVconst [int64(val)])
(Const(32|64)F ...) -> (MOV(F|D)const ...) (Const(32|64)F [val]) => (MOV(F|D)const [float64(val)])
(ConstNil) => (MOVVconst [0]) (ConstNil) => (MOVVconst [0])
(ConstBool ...) -> (MOVVconst ...) (ConstBool [b]) => (MOVVconst [int64(b2i(b))])
(Slicemask <t> x) => (SRAVconst (NEGV <t> x) [63]) (Slicemask <t> x) => (SRAVconst (NEGV <t> x) [63])
@ -161,7 +161,7 @@
(SignExt16to64 ...) => (MOVHreg ...) (SignExt16to64 ...) => (MOVHreg ...)
(SignExt32to64 ...) => (MOVWreg ...) (SignExt32to64 ...) => (MOVWreg ...)
// float <-> int conversion // float <=> int conversion
(Cvt32to32F ...) => (MOVWF ...) (Cvt32to32F ...) => (MOVWF ...)
(Cvt32to64F ...) => (MOVWD ...) (Cvt32to64F ...) => (MOVWD ...)
(Cvt64to32F ...) => (MOVVF ...) (Cvt64to32F ...) => (MOVVF ...)
@ -214,11 +214,11 @@
(Leq32U x y) => (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y))) (Leq32U x y) => (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y)))
(Leq64U x y) => (XOR (MOVVconst [1]) (SGTU x y)) (Leq64U x y) => (XOR (MOVVconst [1]) (SGTU x y))
(OffPtr [off] ptr:(SP)) -> (MOVVaddr [off] ptr) (OffPtr [off] ptr:(SP)) && is32Bit(off) => (MOVVaddr [int32(off)] ptr)
(OffPtr [off] ptr) -> (ADDVconst [off] ptr) (OffPtr [off] ptr) => (ADDVconst [off] ptr)
(Addr ...) -> (MOVVaddr ...) (Addr {sym} base) => (MOVVaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVVaddr {sym} base) (LocalAddr {sym} base _) => (MOVVaddr {sym} base)
// loads // loads
(Load <t> ptr mem) && t.IsBoolean() => (MOVBUload ptr mem) (Load <t> ptr mem) && t.IsBoolean() => (MOVBUload ptr mem)
@ -380,24 +380,17 @@
(InterCall ...) => (CALLinter ...) (InterCall ...) => (CALLinter ...)
// atomic intrinsics // atomic intrinsics
(AtomicLoad8 ...) -> (LoweredAtomicLoad8 ...) (AtomicLoad(8|32|64) ...) => (LoweredAtomicLoad(8|32|64) ...)
(AtomicLoad32 ...) -> (LoweredAtomicLoad32 ...) (AtomicLoadPtr ...) => (LoweredAtomicLoad64 ...)
(AtomicLoad64 ...) -> (LoweredAtomicLoad64 ...)
(AtomicLoadPtr ...) -> (LoweredAtomicLoad64 ...)
(AtomicStore8 ...) -> (LoweredAtomicStore8 ...) (AtomicStore(8|32|64) ...) => (LoweredAtomicStore(8|32|64) ...)
(AtomicStore32 ...) -> (LoweredAtomicStore32 ...) (AtomicStorePtrNoWB ...) => (LoweredAtomicStore64 ...)
(AtomicStore64 ...) -> (LoweredAtomicStore64 ...)
(AtomicStorePtrNoWB ...) -> (LoweredAtomicStore64 ...)
(AtomicExchange32 ...) -> (LoweredAtomicExchange32 ...) (AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...)
(AtomicExchange64 ...) -> (LoweredAtomicExchange64 ...)
(AtomicAdd32 ...) -> (LoweredAtomicAdd32 ...) (AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...)
(AtomicAdd64 ...) -> (LoweredAtomicAdd64 ...)
(AtomicCompareAndSwap32 ...) -> (LoweredAtomicCas32 ...) (AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...)
(AtomicCompareAndSwap64 ...) -> (LoweredAtomicCas64 ...)
// checks // checks
(NilCheck ...) => (LoweredNilCheck ...) (NilCheck ...) => (LoweredNilCheck ...)
@ -444,69 +437,69 @@
(EQ (SGT x (MOVVconst [0])) yes no) => (LEZ x yes no) (EQ (SGT x (MOVVconst [0])) yes no) => (LEZ x yes no)
// fold offset into address // fold offset into address
(ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) -> (MOVVaddr [off1+off2] {sym} ptr) (ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) && is32Bit(off1+int64(off2)) => (MOVVaddr [int32(off1)+int32(off2)] {sym} ptr)
// fold address into load/store // fold address into load/store
(MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBload [off1+off2] {sym} ptr mem) (MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBload [off1+int32(off2)] {sym} ptr mem)
(MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBUload [off1+off2] {sym} ptr mem) (MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBUload [off1+int32(off2)] {sym} ptr mem)
(MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVHload [off1+off2] {sym} ptr mem) (MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHload [off1+int32(off2)] {sym} ptr mem)
(MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVHUload [off1+off2] {sym} ptr mem) (MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHUload [off1+int32(off2)] {sym} ptr mem)
(MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWload [off1+off2] {sym} ptr mem) (MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWload [off1+int32(off2)] {sym} ptr mem)
(MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWUload [off1+off2] {sym} ptr mem) (MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWUload [off1+int32(off2)] {sym} ptr mem)
(MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVVload [off1+off2] {sym} ptr mem) (MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVload [off1+int32(off2)] {sym} ptr mem)
(MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVFload [off1+off2] {sym} ptr mem) (MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVFload [off1+int32(off2)] {sym} ptr mem)
(MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVDload [off1+off2] {sym} ptr mem) (MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVDload [off1+int32(off2)] {sym} ptr mem)
(MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVBstore [off1+off2] {sym} ptr val mem) (MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVBstore [off1+int32(off2)] {sym} ptr val mem)
(MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVHstore [off1+off2] {sym} ptr val mem) (MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVHstore [off1+int32(off2)] {sym} ptr val mem)
(MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVWstore [off1+off2] {sym} ptr val mem) (MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVWstore [off1+int32(off2)] {sym} ptr val mem)
(MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVVstore [off1+off2] {sym} ptr val mem) (MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVVstore [off1+int32(off2)] {sym} ptr val mem)
(MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVFstore [off1+off2] {sym} ptr val mem) (MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVFstore [off1+int32(off2)] {sym} ptr val mem)
(MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVDstore [off1+off2] {sym} ptr val mem) (MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVDstore [off1+int32(off2)] {sym} ptr val mem)
(MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBstorezero [off1+off2] {sym} ptr mem) (MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBstorezero [off1+int32(off2)] {sym} ptr mem)
(MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVHstorezero [off1+off2] {sym} ptr mem) (MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHstorezero [off1+int32(off2)] {sym} ptr mem)
(MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWstorezero [off1+off2] {sym} ptr mem) (MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWstorezero [off1+int32(off2)] {sym} ptr mem)
(MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVVstorezero [off1+off2] {sym} ptr mem) (MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVstorezero [off1+int32(off2)] {sym} ptr mem)
(MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVBload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVBUload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVHload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVHUload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVWload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVWUload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVVload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVVload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVFload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVDload [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) (MOVBstore [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr val mem)
(MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) (MOVHstore [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr val mem)
(MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) (MOVWstore [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr val mem)
(MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVVstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) (MOVVstore [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr val mem)
(MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) (MOVFstore [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr val mem)
(MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) (MOVDstore [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr val mem)
(MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVBstorezero [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVHstorezero [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVWstorezero [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
(MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) -> (MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) =>
(MOVVstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (MOVVstorezero [off1+int32(off2)] {mergeSymTyped(sym1,sym2)} ptr mem)
// store zero // store zero
(MOVBstore [off] {sym} ptr (MOVVconst [0]) mem) => (MOVBstorezero [off] {sym} ptr mem) (MOVBstore [off] {sym} ptr (MOVVconst [0]) mem) => (MOVBstorezero [off] {sym} ptr mem)
@ -643,10 +636,9 @@
(MOVWreg (MOVVconst [c])) => (MOVVconst [int64(int32(c))]) (MOVWreg (MOVVconst [c])) => (MOVVconst [int64(int32(c))])
(MOVWUreg (MOVVconst [c])) => (MOVVconst [int64(uint32(c))]) (MOVWUreg (MOVVconst [c])) => (MOVVconst [int64(uint32(c))])
(MOVVreg (MOVVconst [c])) => (MOVVconst [c]) (MOVVreg (MOVVconst [c])) => (MOVVconst [c])
(LoweredAtomicStore32 ptr (MOVVconst [0]) mem) -> (LoweredAtomicStorezero32 ptr mem) (LoweredAtomicStore(32|64) ptr (MOVVconst [0]) mem) => (LoweredAtomicStorezero(32|64) ptr mem)
(LoweredAtomicStore64 ptr (MOVVconst [0]) mem) -> (LoweredAtomicStorezero64 ptr mem) (LoweredAtomicAdd32 ptr (MOVVconst [c]) mem) && is32Bit(c) => (LoweredAtomicAddconst32 [int32(c)] ptr mem)
(LoweredAtomicAdd32 ptr (MOVVconst [c]) mem) && is32Bit(c) -> (LoweredAtomicAddconst32 [c] ptr mem) (LoweredAtomicAdd64 ptr (MOVVconst [c]) mem) && is32Bit(c) => (LoweredAtomicAddconst64 [c] ptr mem)
(LoweredAtomicAdd64 ptr (MOVVconst [c]) mem) && is32Bit(c) -> (LoweredAtomicAddconst64 [c] ptr mem)
// constant comparisons // constant comparisons
(SGTconst [c] (MOVVconst [d])) && c>d => (MOVVconst [1]) (SGTconst [c] (MOVVconst [d])) && c>d => (MOVVconst [1])

View file

@ -273,9 +273,9 @@ func init() {
{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32
// function calls // function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// duffzero // duffzero
// arg0 = address of memory to zero // arg0 = address of memory to zero

View file

@ -255,9 +255,9 @@ func init() {
{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32
// function calls // function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// atomic ops // atomic ops

View file

@ -11,6 +11,9 @@
(Sub32F ...) => (FSUBS ...) (Sub32F ...) => (FSUBS ...)
(Sub64F ...) => (FSUB ...) (Sub64F ...) => (FSUB ...)
// Combine 64 bit integer multiply and adds
(ADD l:(MULLD x y) z) && objabi.GOPPC64 >= 9 && l.Uses == 1 && clobber(l) => (MADDLD x y z)
(Mod16 x y) => (Mod32 (SignExt16to32 x) (SignExt16to32 y)) (Mod16 x y) => (Mod32 (SignExt16to32 x) (SignExt16to32 y))
(Mod16u x y) => (Mod32u (ZeroExt16to32 x) (ZeroExt16to32 y)) (Mod16u x y) => (Mod32u (ZeroExt16to32 x) (ZeroExt16to32 y))
(Mod8 x y) => (Mod32 (SignExt8to32 x) (SignExt8to32 y)) (Mod8 x y) => (Mod32 (SignExt8to32 x) (SignExt8to32 y))
@ -76,6 +79,23 @@
(Abs ...) => (FABS ...) (Abs ...) => (FABS ...)
(FMA ...) => (FMADD ...) (FMA ...) => (FMADD ...)
// Lowering extension
// Note: we always extend to 64 bits even though some ops don't need that many result bits.
(SignExt8to(16|32|64) ...) => (MOVBreg ...)
(SignExt16to(32|64) ...) => (MOVHreg ...)
(SignExt32to64 ...) => (MOVWreg ...)
(ZeroExt8to(16|32|64) ...) => (MOVBZreg ...)
(ZeroExt16to(32|64) ...) => (MOVHZreg ...)
(ZeroExt32to64 ...) => (MOVWZreg ...)
(Trunc(16|32|64)to8 <t> x) && isSigned(t) => (MOVBreg x)
(Trunc(16|32|64)to8 x) => (MOVBZreg x)
(Trunc(32|64)to16 <t> x) && isSigned(t) => (MOVHreg x)
(Trunc(32|64)to16 x) => (MOVHZreg x)
(Trunc64to32 <t> x) && isSigned(t) => (MOVWreg x)
(Trunc64to32 x) => (MOVWZreg x)
// Lowering constants // Lowering constants
(Const(64|32|16|8) [val]) => (MOVDconst [int64(val)]) (Const(64|32|16|8) [val]) => (MOVDconst [int64(val)])
(Const(32|64)F ...) => (FMOV(S|D)const ...) (Const(32|64)F ...) => (FMOV(S|D)const ...)
@ -107,13 +127,21 @@
// Rotate generation with non-const shift // Rotate generation with non-const shift
// these match patterns from math/bits/RotateLeft[32|64], but there could be others // these match patterns from math/bits/RotateLeft[32|64], but there could be others
(ADD (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y) (ADD (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(ADD (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
( OR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y) ( OR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
( OR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(XOR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y) (XOR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(XOR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
(ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y) (ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y) ( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
(XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
(XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y) (XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
// Lowering rotates // Lowering rotates
(RotateLeft32 x y) => (ROTLW x y) (RotateLeft32 x y) => (ROTLW x y)
(RotateLeft64 x y) => (ROTL x y) (RotateLeft64 x y) => (ROTL x y)
@ -189,11 +217,15 @@
(Rsh64Ux64 x (AND y (MOVDconst [63]))) => (SRD x (ANDconst <typ.Int64> [63] y)) (Rsh64Ux64 x (AND y (MOVDconst [63]))) => (SRD x (ANDconst <typ.Int64> [63] y))
(Rsh64Ux64 x (ANDconst <typ.UInt> [63] y)) => (SRD x (ANDconst <typ.UInt> [63] y)) (Rsh64Ux64 x (ANDconst <typ.UInt> [63] y)) => (SRD x (ANDconst <typ.UInt> [63] y))
(Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) => (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) (Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) => (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64Ux64 x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))) => (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63])))) => (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) (Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63])))) => (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64Ux64 x (SUBFCconst <typ.UInt> [64] (AND <typ.UInt> y (MOVDconst [63])))) => (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (AND y (MOVDconst [63]))) => (SRAD x (ANDconst <typ.Int64> [63] y)) (Rsh64x64 x (AND y (MOVDconst [63]))) => (SRAD x (ANDconst <typ.Int64> [63] y))
(Rsh64x64 x (ANDconst <typ.UInt> [63] y)) => (SRAD x (ANDconst <typ.UInt> [63] y)) (Rsh64x64 x (ANDconst <typ.UInt> [63] y)) => (SRAD x (ANDconst <typ.UInt> [63] y))
(Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) => (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) (Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) => (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))) => (SRAD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63])))) => (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) (Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63])))) => (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (SUBFCconst <typ.UInt> [64] (AND <typ.UInt> y (MOVDconst [63])))) => (SRAD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Lsh64x64 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) (Lsh64x64 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64]))))
(Rsh64x64 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) (Rsh64x64 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64]))))
@ -205,12 +237,16 @@
(Rsh32Ux64 x (AND y (MOVDconst [31]))) => (SRW x (ANDconst <typ.Int32> [31] y)) (Rsh32Ux64 x (AND y (MOVDconst [31]))) => (SRW x (ANDconst <typ.Int32> [31] y))
(Rsh32Ux64 x (ANDconst <typ.UInt> [31] y)) => (SRW x (ANDconst <typ.UInt> [31] y)) (Rsh32Ux64 x (ANDconst <typ.UInt> [31] y)) => (SRW x (ANDconst <typ.UInt> [31] y))
(Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) => (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) (Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) => (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32Ux64 x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))) => (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31])))) => (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) (Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31])))) => (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32Ux64 x (SUBFCconst <typ.UInt> [32] (AND <typ.UInt> y (MOVDconst [31])))) => (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (AND y (MOVDconst [31]))) => (SRAW x (ANDconst <typ.Int32> [31] y)) (Rsh32x64 x (AND y (MOVDconst [31]))) => (SRAW x (ANDconst <typ.Int32> [31] y))
(Rsh32x64 x (ANDconst <typ.UInt> [31] y)) => (SRAW x (ANDconst <typ.UInt> [31] y)) (Rsh32x64 x (ANDconst <typ.UInt> [31] y)) => (SRAW x (ANDconst <typ.UInt> [31] y))
(Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) => (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) (Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) => (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))) => (SRAW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31])))) => (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) (Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31])))) => (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (SUBFCconst <typ.UInt> [32] (AND <typ.UInt> y (MOVDconst [31])))) => (SRAW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) (Rsh32x64 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32]))))
(Rsh32Ux64 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) (Rsh32Ux64 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32]))))
@ -273,18 +309,11 @@
(Rsh8Ux8 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) (Rsh8Ux8 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8]))))
(Lsh8x8 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) (Lsh8x8 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8]))))
// Cleaning up shift ops when input is masked // Cleaning up shift ops
(MaskIfNotCarry (ADDconstForCarry [c] (ANDconst [d] _))) && c < 0 && d > 0 && int64(c) + d < 0 => (MOVDconst [-1])
(ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPU (ANDconst [d] y) (MOVDconst [c]))) && c >= d => (ANDconst [d] y) (ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPU (ANDconst [d] y) (MOVDconst [c]))) && c >= d => (ANDconst [d] y)
(ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPUconst [c] (ANDconst [d] y))) && c >= d => (ANDconst [d] y) (ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPUconst [c] (ANDconst [d] y))) && c >= d => (ANDconst [d] y)
(ORN x (MOVDconst [-1])) => x (ORN x (MOVDconst [-1])) => x
(ADDconstForCarry [c] (MOVDconst [d])) && c < 0 && (c < 0 || int64(c) + d >= 0) => (FlagCarryClear)
(ADDconstForCarry [c] (MOVDconst [d])) && c < 0 && c >= 0 && int64(c) + d < 0 => (FlagCarrySet)
(MaskIfNotCarry (FlagCarrySet)) => (MOVDconst [0])
(MaskIfNotCarry (FlagCarryClear)) => (MOVDconst [-1])
(S(RAD|RD|LD) x (MOVDconst [c])) => (S(RAD|RD|LD)const [c&63 | (c>>6&1*63)] x) (S(RAD|RD|LD) x (MOVDconst [c])) => (S(RAD|RD|LD)const [c&63 | (c>>6&1*63)] x)
(S(RAW|RW|LW) x (MOVDconst [c])) => (S(RAW|RW|LW)const [c&31 | (c>>5&1*31)] x) (S(RAW|RW|LW) x (MOVDconst [c])) => (S(RAW|RW|LW)const [c&31 | (c>>5&1*31)] x)
@ -303,8 +332,8 @@
(Ctz16 x) => (POPCNTW (MOVHZreg (ANDN <typ.Int16> (ADDconst <typ.Int16> [-1] x) x))) (Ctz16 x) => (POPCNTW (MOVHZreg (ANDN <typ.Int16> (ADDconst <typ.Int16> [-1] x) x)))
(Ctz8 x) => (POPCNTB (MOVBZreg (ANDN <typ.UInt8> (ADDconst <typ.UInt8> [-1] x) x))) (Ctz8 x) => (POPCNTB (MOVBZreg (ANDN <typ.UInt8> (ADDconst <typ.UInt8> [-1] x) x)))
(BitLen64 x) => (SUB (MOVDconst [64]) (CNTLZD <typ.Int> x)) (BitLen64 x) => (SUBFCconst [64] (CNTLZD <typ.Int> x))
(BitLen32 x) => (SUB (MOVDconst [32]) (CNTLZW <typ.Int> x)) (BitLen32 x) => (SUBFCconst [32] (CNTLZW <typ.Int> x))
(PopCount64 ...) => (POPCNTD ...) (PopCount64 ...) => (POPCNTD ...)
(PopCount32 x) => (POPCNTW (MOVWZreg x)) (PopCount32 x) => (POPCNTW (MOVWZreg x))
@ -768,16 +797,40 @@
(MOVWreg y:(MOVWZreg x)) => (MOVWreg x) (MOVWreg y:(MOVWZreg x)) => (MOVWreg x)
(MOVWZreg y:(MOVWreg x)) => (MOVWZreg x) (MOVWZreg y:(MOVWreg x)) => (MOVWZreg x)
// Truncate then logical then truncate: omit first, lesser or equal truncate
(MOVWZreg ((OR|XOR|AND) <t> x (MOVWZreg y))) => (MOVWZreg ((OR|XOR|AND) <t> x y))
(MOVHZreg ((OR|XOR|AND) <t> x (MOVWZreg y))) => (MOVHZreg ((OR|XOR|AND) <t> x y))
(MOVHZreg ((OR|XOR|AND) <t> x (MOVHZreg y))) => (MOVHZreg ((OR|XOR|AND) <t> x y))
(MOVBZreg ((OR|XOR|AND) <t> x (MOVWZreg y))) => (MOVBZreg ((OR|XOR|AND) <t> x y))
(MOVBZreg ((OR|XOR|AND) <t> x (MOVHZreg y))) => (MOVBZreg ((OR|XOR|AND) <t> x y))
(MOVBZreg ((OR|XOR|AND) <t> x (MOVBZreg y))) => (MOVBZreg ((OR|XOR|AND) <t> x y))
(MOV(B|H|W)Zreg z:(ANDconst [c] (MOVBZload ptr x))) => z
(MOVBZreg z:(AND y (MOVBZload ptr x))) => z
(MOV(H|W)Zreg z:(ANDconst [c] (MOVHZload ptr x))) => z
(MOVHZreg z:(AND y (MOVHZload ptr x))) => z
(MOVWZreg z:(ANDconst [c] (MOVWZload ptr x))) => z
(MOVWZreg z:(AND y (MOVWZload ptr x))) => z
// Arithmetic constant ops // Arithmetic constant ops
(ADD x (MOVDconst [c])) && is32Bit(c) => (ADDconst [c] x) (ADD x (MOVDconst [c])) && is32Bit(c) => (ADDconst [c] x)
(ADDconst [c] (ADDconst [d] x)) && is32Bit(c+d) => (ADDconst [c+d] x) (ADDconst [c] (ADDconst [d] x)) && is32Bit(c+d) => (ADDconst [c+d] x)
(ADDconst [0] x) => x (ADDconst [0] x) => x
(SUB x (MOVDconst [c])) && is32Bit(-c) => (ADDconst [-c] x) (SUB x (MOVDconst [c])) && is32Bit(-c) => (ADDconst [-c] x)
// TODO deal with subtract-from-const
(ADDconst [c] (MOVDaddr [d] {sym} x)) && is32Bit(c+int64(d)) => (MOVDaddr [int32(c+int64(d))] {sym} x) (ADDconst [c] (MOVDaddr [d] {sym} x)) && is32Bit(c+int64(d)) => (MOVDaddr [int32(c+int64(d))] {sym} x)
// Subtract from (with carry, but ignored) constant.
// Note, these clobber the carry bit.
(SUB (MOVDconst [c]) x) && is32Bit(c) => (SUBFCconst [c] x)
(SUBFCconst [c] (NEG x)) => (ADDconst [c] x)
(SUBFCconst [c] (SUBFCconst [d] x)) && is32Bit(c-d) => (ADDconst [c-d] x)
(SUBFCconst [0] x) => (NEG x)
(ADDconst [c] (SUBFCconst [d] x)) && is32Bit(c+d) => (SUBFCconst [c+d] x)
(NEG (ADDconst [c] x)) && is32Bit(-c) => (SUBFCconst [-c] x)
(NEG (SUBFCconst [c] x)) && is32Bit(-c) => (ADDconst [-c] x)
// Use register moves instead of stores and loads to move int<=>float values // Use register moves instead of stores and loads to move int<=>float values
// Common with math Float64bits, Float64frombits // Common with math Float64bits, Float64frombits
(MOVDload [off] {sym} ptr (FMOVDstore [off] {sym} ptr x _)) => (MFVSRD x) (MOVDload [off] {sym} ptr (FMOVDstore [off] {sym} ptr x _)) => (MFVSRD x)
@ -928,23 +981,6 @@
(AtomicAnd8 ...) => (LoweredAtomicAnd8 ...) (AtomicAnd8 ...) => (LoweredAtomicAnd8 ...)
(AtomicOr8 ...) => (LoweredAtomicOr8 ...) (AtomicOr8 ...) => (LoweredAtomicOr8 ...)
// Lowering extension
// Note: we always extend to 64 bits even though some ops don't need that many result bits.
(SignExt8to(16|32|64) ...) => (MOVBreg ...)
(SignExt16to(32|64) ...) => (MOVHreg ...)
(SignExt32to64 ...) => (MOVWreg ...)
(ZeroExt8to(16|32|64) ...) => (MOVBZreg ...)
(ZeroExt16to(32|64) ...) => (MOVHZreg ...)
(ZeroExt32to64 ...) => (MOVWZreg ...)
(Trunc(16|32|64)to8 <t> x) && isSigned(t) => (MOVBreg x)
(Trunc(16|32|64)to8 x) => (MOVBZreg x)
(Trunc(32|64)to16 <t> x) && isSigned(t) => (MOVHreg x)
(Trunc(32|64)to16 x) => (MOVHZreg x)
(Trunc64to32 <t> x) && isSigned(t) => (MOVWreg x)
(Trunc64to32 x) => (MOVWZreg x)
(Slicemask <t> x) => (SRADconst (NEG <t> x) [63]) (Slicemask <t> x) => (SRADconst (NEG <t> x) [63])
// Note that MOV??reg returns a 64-bit int, x is not necessarily that wide // Note that MOV??reg returns a 64-bit int, x is not necessarily that wide
@ -975,6 +1011,20 @@
(MOVWreg (MOVDconst [c])) => (MOVDconst [int64(int32(c))]) (MOVWreg (MOVDconst [c])) => (MOVDconst [int64(int32(c))])
(MOVWZreg (MOVDconst [c])) => (MOVDconst [int64(uint32(c))]) (MOVWZreg (MOVDconst [c])) => (MOVDconst [int64(uint32(c))])
// Implement clrsldi and clrslwi extended mnemonics as described in
// ISA 3.0 section C.8. AuxInt field contains values needed for
// the instructions, packed together since there is only one available.
(SLDconst [c] z:(MOVBZreg x)) && c < 8 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,56,63,64)] x)
(SLDconst [c] z:(MOVHZreg x)) && c < 16 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,48,63,64)] x)
(SLDconst [c] z:(MOVWZreg x)) && c < 32 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,32,63,64)] x)
(SLDconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x)
(SLDconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x)
(SLWconst [c] z:(MOVBZreg x)) && z.Uses == 1 && c < 8 => (CLRLSLWI [newPPC64ShiftAuxInt(c,24,31,32)] x)
(SLWconst [c] z:(MOVHZreg x)) && z.Uses == 1 && c < 16 => (CLRLSLWI [newPPC64ShiftAuxInt(c,16,31,32)] x)
(SLWconst [c] z:(MOVWZreg x)) && z.Uses == 1 && c < 24 => (CLRLSLWI [newPPC64ShiftAuxInt(c,8,31,32)] x)
(SLWconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x)
(SLWconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x)
// Lose widening ops fed to stores // Lose widening ops fed to stores
(MOVBstore [off] {sym} ptr (MOV(B|BZ|H|HZ|W|WZ)reg x) mem) => (MOVBstore [off] {sym} ptr x mem) (MOVBstore [off] {sym} ptr (MOV(B|BZ|H|HZ|W|WZ)reg x) mem) => (MOVBstore [off] {sym} ptr x mem)

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