diff --git a/.gitignore b/.gitignore index 6851e147d33877..7173067a759c6f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ misc/cgo/stdio/run.out misc/cgo/testso/main src/cmd/cgo/zdefaultcc.go src/cmd/go/zdefaultcc.go +src/cmd/go/zosarch.go src/cmd/internal/obj/zbootstrap.go src/go/build/zcgo.go src/go/doc/headscan diff --git a/AUTHORS b/AUTHORS index 34a78e5bd08635..d6b72718bcc4ad 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,13 +14,17 @@ A Medium Corporation Aamir Khan Aaron France Aaron Torres +Abe Haskins Abhinav Gupta Adrian Nos Adrian O'Grady Adrien Bustany Aécio Júnior Ahmed Waheed Moanes +Ahmy Yulrizka +Aiden Scandella Ainar Garipov +Akihiro Suda Akshat Kumar Alan Shreve Albert Strasheim @@ -28,6 +32,7 @@ Alberto Bertogli Alberto Donizetti Alberto García Hierro Aleksandar Dezelin +Alessandro Arzilli Alex A Skinner Alex Brainman Alex Jin @@ -50,8 +55,10 @@ Alexey Borzenkov Alexey Palazhchenko Aliaksandr Valialkin Alif Rachmawadi +Amazon.com, Inc Amir Mohammad Saied Amrut Joshi +Andre Nathan Andrei Korzhevskii Andrei Vieru Andrew Balholm @@ -85,6 +92,8 @@ Anthony Starks Apisak Darakananda Aram Hăvărneanu Areski Belaid +Arlo Breault +ARM Ltd. Arnaud Ysmal Arne Hormann Arnout Engelen @@ -92,6 +101,8 @@ Aron Nopanen Artyom Pervukhin Arvindh Rajesh Tamilmani Ato Araki +Audrey Lim +Augusto Roman Aulus Egnatius Varialus awaw fumin Aymerick Jéhanne @@ -107,6 +118,8 @@ Bjorn Tipling Blake Gentry Blake Mizerany Bobby Powers +Brady Catherman +Brady Sullivan Brendan Daniel Tracey Brett Cannon Brian Dellisanti @@ -140,17 +153,21 @@ Christoffer Buchholz Christoph Hack Christopher Cahoon Christopher Guiney +Christopher Nelson Christopher Nielsen Christopher Redden Christopher Wedgwood CL Sung Clement Skau CloudFlare Inc. +Colin Edwards Colin Kennedy +Conrad Irwin Conrad Meyer CoreOS, Inc. Corey Thomasson Cristian Staretu +Currant Damian Gryski Dan Caddigan Dan Callahan @@ -164,9 +181,12 @@ Daniel Lidén Daniel Morsing Daniel Ortiz Pereira da Silva Daniel Skinner +Daniel Speichert Daniel Theophanes Darren Elwood +Datong Sun Dave Cheney +David Brophy David Bürgin <676c7473@gmail.com> David Calavera David du Colombier <0intro@gmail.com> @@ -176,21 +196,26 @@ David Howden David Jakob Fritz David Leon Gil David R. Jenni +David Sansome David Thomas David Titarenco Davies Liu Dean Prichard Denis Bernard Denis Brandolini +Denys Honsiorovskyi Derek Buitenhuis Derek Parker +Derek Shockey Develer SRL Devon H. O'Dell Dhiru Kholia Didier Spezia Dimitri Tcaciuc Dirk Gadsden +Diwaker Gupta Dmitri Shuralyov +Dmitriy Dudkin Dmitriy Shelenin Dmitry Chestnykh Dmitry Savintsev @@ -200,6 +225,7 @@ Donald Huang Donovan Hide Dropbox, Inc. Duncan Holm +Dustin Herbison Dustin Sallings Dustin Shields-Cloues Dvir Volk @@ -220,6 +246,7 @@ Erik Aigner Erik Dubbelboer Erik St. Martin Erik Westrup +Ernest Chiang Esko Luontola Evan Phoenix Evan Shaw @@ -241,6 +268,7 @@ Francisco Souza Frederick Kelly Mayle III Fredrik Enestad Frithjof Schulze +Frits van Bommel Gabriel Aszalos Gary Burd Gaurish Sharma @@ -266,30 +294,37 @@ Hajime Hoshi Hari haran Hariharan Srinath Harley Laue +Harshavardhana Håvard Haugen Hector Chu Hector Martin Cantero Henning Schmiedehausen Henrik Edwards Herbert Georg Fischer +Hironao OTSUBO Hiroshi Ioka +Hitoshi Mitake +Holden Huang Hong Ruiqi Hsin-Ho Yeh Hu Keping Ian Gudger IBM Icarus Sparry +Idora Shinatose Igneous Systems, Inc. Igor Dolzhikov INADA Naoki Ingo Krabbe Ingo Oeser Intel Corporation +Irieda Noboru Isaac Wagner Ivan Ukhov Jae Kwon Jakob Borg Jakub Ryszard Czarnowicz +James Bardin James David Chalfant James Fysh James Gray @@ -299,6 +334,7 @@ James Schofield James Sweet James Toy James Whitehead +Jamil Djadala Jan H. Hosang Jan Mercl <0xjnml@gmail.com> Jan Mercl @@ -315,6 +351,7 @@ Jeff Sickel Jeff Wendling Jens Frederich Jeremy Jackins +Jess Frazelle Jihyun Yu Jim McGrath Jimmy Zelinskie @@ -323,16 +360,21 @@ Jingguo Yao Jiong Du Joakim Sernbrant Joe Harrison +Joe Henke Joe Poirier Joe Shaw +Joe Sylve Joe Tsai Joel Stemmer +Johan Sageryd John Asmuth John C Barstow John Graham-Cumming John Howard Palevich +John Jeffery John Jenkins John Potocny +John Schnake John Shahid John Tuley Jonathan Boulle @@ -366,8 +408,13 @@ Kelvin Foo Chuan Lyi Ken Friedenbach Ken Rockot Ken Sedgwick +Kenji Kaneda +Kenneth Shaw Kenny Grant Kevin Ballard +Kevin Burke +Kevin Kirsche +Kevin Vu Klaus Post Konstantin Shaposhnikov KPCompass, Inc. @@ -379,12 +426,14 @@ Kyle Lemons L Campbell Lai Jiangshan Larz Conwell +Lee Hinman Lee Packham Lewin Bormann Liberty Fund Inc Linaro Limited Lloyd Dewolf Lorenzo Stoakes +Luan Santos Luca Greco Lucien Stuker Lucio De Re @@ -397,6 +446,7 @@ Manuel Mendez Marc Weistroff Marco Hennings Mark Bucciarelli +Mark Severson Mark Theunissen Marko Juhani Silokunnas Marko Tiikkaja @@ -404,12 +454,14 @@ Markover Inc. DBA Poptip Markus Duft Markus Sonderegger Markus Zimmermann +Martin Garton Martin Möhrmann Martin Neubauer Martin Olsson Marvin Stenger Mateusz Czapliński Mathias Beke +Mathias Leppich Mathieu Lonjaret Mats Lidell Matt Aimonetti @@ -419,6 +471,7 @@ Matt Jibson Matt Joiner Matt Layher Matt Reiferson +Matt Robenolt Matt T. Proud Matt Williams Matthew Brennan @@ -426,6 +479,7 @@ Matthew Cottingham Matthew Holt Matthew Horsnell Maxim Khitrov +Maxwell Krohn Meir Fischer Meng Zhuo Meteor Development Group @@ -439,6 +493,7 @@ Michael Hoisie Michael Käufl Michael Lewis Michael MacInnis +Michael McConville Michael Pearson Michael Schaller Michael Stapelberg @@ -451,15 +506,19 @@ Mihai Borobocea Mikael Tillenius Mike Andrews Mike Rosset +Mikhail Gusarov Mikhail Panchenko Miki Tebeka Mikio Hara Mikkel Krautz Miquel Sabaté Solà Mohit Agarwal +Monty Taylor Moov Corporation Moriyoshi Koizumi +Morten Siebuhr Môshe van der Sterre +Muhammed Uluyol Nan Deng Nathan John Youngman Nathan Otterness @@ -467,17 +526,23 @@ Nathan P Finch Nathan VanBenschoten Nathan Youngman Neelesh Chandola +Netflix, Inc. Nevins Bartolomeo ngmoco, LLC +Niall Sheridan Nicholas Katsaros Nicholas Presta Nicholas Sullivan Nicholas Waples Nick Craig-Wood +Nick Patavalis +Nick Petroni Nicolas Kaiser Nicolas Owens Nicolas S. Dade +Niels Widger Nigel Kerr +Niko Dziemba Nikolay Turpitko Noah Campbell Norberto Lopes @@ -486,8 +551,11 @@ Oling Cat Oliver Hookins Olivier Antoine Olivier Duperray +Olivier Poitrey Olivier Saingre Oracle +Orange +Özgür Kesim Padraig Kitterick Palm Stone Games Paolo Giarrusso @@ -523,10 +591,13 @@ Péter Szilágyi Peter Waldschmidt Peter Waller Peter Williams +Philip Hofer Philip K. Warren +Pierre Durand Pierre Roullon Pieter Droogendijk Pietro Gagliardi +Prashant Varanasi Preetam Jinka Quan Yong Zhai Quentin Perez @@ -538,9 +609,11 @@ Ralph Corderoy Red Hat, Inc. Reinaldo de Souza Jr Rémy Oudompheng +Ricardo Padilha Richard Barnes Richard Crowley Richard Eric Gavaletz +Richard Miller Richard Musiol Rick Arnold Risto Jaakko Saarelma @@ -556,6 +629,7 @@ Rodrigo Moraes de Oliveira Rodrigo Rafael Monti Kochenburger Roger Pau Monné Roger Peppe +Roland Shoemaker Ron Hashimoto Ron Minnich Ross Light @@ -567,8 +641,11 @@ Ryan Seys Ryan Slade S.Çağlar Onur Salmān Aljammāz +Sam Hug +Sam Whited Sanjay Menakuru Scott Barron +Scott Bell Scott Ferguson Scott Lawrence Sebastien Binet @@ -577,17 +654,21 @@ Sergei Skorobogatov Sergey 'SnakE' Gromov Sergio Luis O. B. Correia Seth Hoenig +Shahar Kohanim Shane Hansen Shaozhen Ding Shawn Smith Shenghou Ma +Shinji Tanaka Shivakumar GN Silvan Jegen +Simon Jefford Simon Whitehead Sokolov Yura Spencer Nelson Spring Mc Square, Inc. +Sridhar Venkatakrishnan StalkR Stan Schwertly Stefan Nilsson @@ -605,6 +686,7 @@ Szabolcs Nagy Tad Glines Taj Khattra Takeshi YAMANASHI <9.nashi@gmail.com> +Tal Shprecher Tamir Duberstein Tarmigan Casebolt Taru Karttunen @@ -615,9 +697,12 @@ Thomas Alan Copeland Thomas Desrosiers Thomas Kappler Thorben Krueger +Tilman Dilo Tim Cooijmans +Tim Ebringer Timo Savola Timo Truyts +Timothy Studd Tobias Columbus Todd Neal Tom Heng @@ -634,12 +719,15 @@ Tyler Treat Ugorji Nwoke Ulf Holm Nielsen Ulrich Kunitz +Upthere, Inc. Uriel Mangado +Vadim Grek Vadim Vygonets Vincent Ambo Vincent Batts Vincent Vanackere Vinu Rajashekhar +Vishvananda Ishaya Vladimir Nikishenko Volker Dobler Wei Guangjing @@ -648,6 +736,7 @@ William Josephson William Orr Xia Bin Xing Xing +Xudong Zhang Yahoo Inc. Yann Kerhervé Yao Zhang @@ -661,6 +750,7 @@ Yoshiyuki Kanno Yusuke Kagiwada Yuusei Kuwana Yuval Pavel Zholkover +Zemanta d.o.o. Ziad Hatahet Zorion Arrizabalaga 申习之 diff --git a/CONTRIBUTORS b/CONTRIBUTORS index e225afec80c6ee..6845a392680b61 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -37,6 +37,7 @@ Aaron France Aaron Jacobs Aaron Kemp Aaron Torres +Abe Haskins Abhinav Gupta Adam Langley Adrian Nos @@ -44,7 +45,10 @@ Adrian O'Grady Adrien Bustany Aécio Júnior Ahmed Waheed Moanes +Ahmy Yulrizka +Aiden Scandella Ainar Garipov +Akihiro Suda Akshat Kumar Alan Donovan Alan Shreve @@ -53,6 +57,7 @@ Alberto Bertogli Alberto Donizetti Alberto García Hierro Aleksandar Dezelin +Alessandro Arzilli Alex A Skinner Alex Brainman Alex Bramley @@ -60,6 +65,7 @@ Alex Jin Alex Plugaru Alex Schroeder Alex Sergeyev +Alex Vaghin Alexander Demakin Alexander Larsson Alexander Morozov @@ -80,6 +86,7 @@ Aliaksandr Valialkin Alif Rachmawadi Amir Mohammad Saied Amrut Joshi +Andre Nathan Andrea Spadaccini Andreas Jellinghaus Andrei Korzhevskii @@ -98,6 +105,7 @@ Andrew Pritchard Andrew Radev Andrew Skiba Andrew Szeto +Andrew Werner Andrew Wilkins Andrew Williams Andrey Mirtchovski @@ -119,6 +127,7 @@ Apisak Darakananda Aram Hăvărneanu Areski Belaid Arkadi Pyuro +Arlo Breault Arnaud Ysmal Arne Hormann Arnout Engelen @@ -127,6 +136,8 @@ Artyom Pervukhin Arvindh Rajesh Tamilmani Asim Shankar Ato Araki +Audrey Lim +Augusto Roman Aulus Egnatius Varialus Austin Clements awaw fumin @@ -139,6 +150,7 @@ Ben Lynn Ben Olive Benjamin Black Benjamin Prosnitz +Benjamin Wester Benny Siegert Benoit Sigoure Berengar Lehr @@ -152,6 +164,8 @@ Blake Mizerany Bobby Powers Brad Fitzpatrick Brad Garcia +Brady Catherman +Brady Sullivan Brandon Gilmore Brendan Daniel Tracey Brendan O'Dea @@ -163,8 +177,10 @@ Brian Ketelsen Brian Slesinsky Brian Smith Bryan C. Mills +Bryan Chan Bryan Ford Caine Tighe +Caio Marcelo de Oliveira Filho Caleb Spare Carl Chatfield Carl Jackson @@ -175,6 +191,7 @@ Carlos Cirello Cary Hull Case Nelson Casey Marshall +Catalin Nicutar Catalin Patulea Cedric Staub Cezar Sá Espinola @@ -182,6 +199,7 @@ ChaiShushan Charles L. Dorian Charles Lee Charles Weill +Cherry Zhang Chris Broadfoot Chris Dollin Chris Farmiloe @@ -193,25 +211,31 @@ Chris Kastorff Chris Lennert Chris Manghane Chris McGee +Chris Zou Christian Himpel Christine Hansmann Christoffer Buchholz Christoph Hack Christopher Cahoon Christopher Guiney +Christopher Nelson Christopher Nielsen Christopher Redden Christopher Swenson Christopher Wedgwood +Christy Perez CL Sung Clement Skau Colby Ranger Colin Cross +Colin Edwards Colin Kennedy +Conrad Irwin Conrad Meyer Corey Thomasson Cosmos Nicolaou Cristian Staretu +Cuihtlauac ALVARADO Damian Gryski Damien Neil Dan Caddigan @@ -229,8 +253,10 @@ Daniel Morsing Daniel Nadasi Daniel Ortiz Pereira da Silva Daniel Skinner +Daniel Speichert Daniel Theophanes Darren Elwood +Datong Sun Dave Borowitz Dave Bort Dave Cheney @@ -239,6 +265,7 @@ Dave Grijalva David Anderson David Barnett David Benjamin +David Brophy David Bürgin <676c7473@gmail.com> David Calavera David Chase @@ -254,6 +281,7 @@ David Leon Gil David McLeish David Presotto David R. Jenni +David Sansome David Symonds David Thomas David Titarenco @@ -261,15 +289,19 @@ Davies Liu Dean Prichard Denis Bernard Denis Brandolini +Denys Honsiorovskyi Derek Buitenhuis Derek Che Derek Parker +Derek Shockey Devon H. O'Dell Dhiru Kholia Didier Spezia Dimitri Tcaciuc Dirk Gadsden +Diwaker Gupta Dmitri Shuralyov +Dmitriy Dudkin Dmitriy Shelenin Dmitriy Vyukov Dmitry Chestnykh @@ -279,8 +311,11 @@ Dominik Honnef Dominik Vogt Donald Huang Donovan Hide +Doug Anderson Drew Hintz Duncan Holm +Dustin Carlino +Dustin Herbison Dustin Long Dustin Sallings Dustin Shields-Cloues @@ -304,7 +339,9 @@ Erik Aigner Erik Dubbelboer Erik St. Martin Erik Westrup +Ernest Chiang Esko Luontola +Ethan Burns Evan Broder Evan Brown Evan Kroske @@ -331,11 +368,13 @@ Francisco Souza Frederick Kelly Mayle III Fredrik Enestad Frithjof Schulze +Frits van Bommel Fumitoshi Ukai Gaal Yahas Gabriel Aszalos Garrick Evans Gary Burd +Gary Elliott Gaurish Sharma Gautham Thambidorai Geert-Johan Riemer @@ -359,17 +398,22 @@ Gustavo Franco Gustavo Niemeyer Gwenael Treguier Hajime Hoshi +Hallgrimur Gunnarsson Han-Wen Nienhuys Hari haran Hariharan Srinath Harley Laue +Harshavardhana Håvard Haugen Hector Chu Hector Martin Cantero Henning Schmiedehausen Henrik Edwards Herbert Georg Fischer +Hironao OTSUBO Hiroshi Ioka +Hitoshi Mitake +Holden Huang Hong Ruiqi Hossein Sheikh Attar Hsin-Ho Yeh @@ -378,11 +422,13 @@ Hyang-Ah Hana Kim Ian Gudger Ian Lance Taylor Icarus Sparry +Idora Shinatose Igor Dolzhikov Ilya Tocar INADA Naoki Ingo Krabbe Ingo Oeser +Irieda Noboru Isaac Wagner Ivan Krasin Ivan Ukhov @@ -394,6 +440,8 @@ Jakob Borg Jakub Čajka Jakub Ryszard Czarnowicz James Aguilar +James Bardin +James Chacon James David Chalfant James Fysh James Gray @@ -408,6 +456,7 @@ James Whitehead Jamie Gennis Jamie Turner Jamie Wilkinson +Jamil Djadala Jan H. Hosang Jan Kratochvil Jan Mercl <0xjnml@gmail.com> @@ -422,6 +471,7 @@ Jason Travis Jay Weisskopf Jean-Marc Eurin Jed Denlea +Jeff Craig Jeff Hodges Jeff R. Allen Jeff Sickel @@ -430,6 +480,7 @@ Jens Frederich Jeremiah Harmsen Jeremy Jackins Jeremy Schlatter +Jess Frazelle Jihyun Yu Jim Cote Jim McGrath @@ -439,12 +490,15 @@ Jingguo Yao Jiong Du Joakim Sernbrant Joe Harrison +Joe Henke Joe Poirier Joe Shaw +Joe Sylve Joe Tsai Joel Sing Joel Stemmer Johan Euphrosine +Johan Sageryd John Asmuth John Beisley John C Barstow @@ -452,12 +506,15 @@ John DeNero John Dethridge John Graham-Cumming John Howard Palevich +John Jeffery John Jenkins John Newlin John Potocny +John Schnake John Shahid John Tuley Jonathan Allie +Jonathan Amsterdam Jonathan Boulle Jonathan Feinberg Jonathan Gold @@ -481,11 +538,14 @@ Jostein Stuhaug JP Sugarbroad JT Olds Jukka-Pekka Kekkonen +Julia Hansbrough Julian Phillips Julien Schmidt Jungho Ahn +Jure Ham Justin Nuß Kai Backman +Kamal Aboul-Hosn Kamil Kisiel Kang Hu Kato Kazuyoshi @@ -502,10 +562,15 @@ Ken Friedenbach Ken Rockot Ken Sedgwick Ken Thompson +Kenji Kaneda +Kenneth Shaw Kenny Grant Kevin Ballard +Kevin Burke +Kevin Kirsche Kevin Klues Kevin Malachowski +Kevin Vu Kim Shrier Kirklin McDonald Klaus Post @@ -519,11 +584,13 @@ L Campbell Lai Jiangshan Larry Hosken Larz Conwell +Lee Hinman Lee Packham Lewin Bormann Lloyd Dewolf Lorenzo Stoakes Louis Kruger +Luan Santos Luca Greco Lucien Stuker Lucio De Re @@ -539,11 +606,13 @@ Manu Garg Manu S Ajith Manuel Mendez Marc Weistroff +Marc-Antoine Ruel Marcel van Lohuizen Marco Hennings Marga Manterola Marius Nuennerich Mark Bucciarelli +Mark Severson Mark Theunissen Mark Zavislak Marko Juhani Silokunnas @@ -552,12 +621,14 @@ Marko Tiikkaja Markus Duft Markus Sonderegger Markus Zimmermann +Martin Garton Martin Möhrmann Martin Neubauer Martin Olsson Marvin Stenger Mateusz Czapliński Mathias Beke +Mathias Leppich Mathieu Lonjaret Mats Lidell Matt Aimonetti @@ -569,6 +640,7 @@ Matt Joiner Matt Jones Matt Layher Matt Reiferson +Matt Robenolt Matt T. Proud Matt Williams Matthew Brennan @@ -579,6 +651,7 @@ Matthew Horsnell Maxim Khitrov Maxim Pimenov Maxim Ushakov +Maxwell Krohn Meir Fischer Meng Zhuo Mhd Sulhan @@ -595,9 +668,12 @@ Michael Lewis Michael MacInnis Michael Marineau Michael Matloob +Michael McConville Michael McGreevy +Michael Munday Michael Pearson Michael Piatek +Michael Pratt Michael Schaller Michael Shields Michael Stapelberg @@ -608,22 +684,28 @@ Michal Bohuslávek Michal Cierniak Michał Derkacz Michalis Kargakis +Michel Lespinasse Miek Gieben Mihai Borobocea Mikael Tillenius Mike Andrews +Mike Danese Mike Rosset Mike Samuel Mike Solomon +Mikhail Gusarov Mikhail Panchenko Miki Tebeka Mikio Hara Mikkel Krautz Miquel Sabaté Solà Mohit Agarwal +Monty Taylor Moriyoshi Koizumi +Morten Siebuhr Môshe van der Sterre Mrunal Patel +Muhammed Uluyol Nan Deng Nathan John Youngman Nathan Otterness @@ -633,17 +715,22 @@ Nathan Youngman Nathan(yinian) Hu Neelesh Chandola Nevins Bartolomeo +Niall Sheridan Nicholas Katsaros Nicholas Presta Nicholas Sullivan Nicholas Waples Nick Cooper Nick Craig-Wood +Nick Patavalis +Nick Petroni Nicolas Kaiser Nicolas Owens Nicolas S. Dade +Niels Widger Nigel Kerr Nigel Tao +Niko Dziemba Nikolay Turpitko Noah Campbell Nodir Turakulov @@ -653,7 +740,10 @@ Oling Cat Oliver Hookins Olivier Antoine Olivier Duperray +Olivier Poitrey Olivier Saingre +Omar Jarjur +Özgür Kesim Padraig Kitterick Paolo Giarrusso Paolo Martini @@ -678,6 +768,7 @@ Paul Rosania Paul Sbarra Paul Smith Paul van Brouwershaven +Paul Wankadia Pavel Paulau Pavel Zinovkin Pawel Knap @@ -688,6 +779,7 @@ Petar Maymounkov Peter Armitage Peter Collingbourne Peter Froehlich +Peter Gonda Peter Kleiweg Peter McKenzie Peter Moody @@ -701,13 +793,17 @@ Peter Waller Peter Weinberger Peter Williams Phil Pennock +Philip Hofer Philip K. Warren +Pierre Durand Pierre Roullon Pieter Droogendijk Pietro Gagliardi +Prashant Varanasi Preetam Jinka Quan Yong Zhai Quentin Perez +Quentin Smith Quoc-Viet Nguyen Rahul Chaudhry Raif S. Naffah @@ -717,12 +813,16 @@ Raph Levien Raul Silvera Reinaldo de Souza Jr Rémy Oudompheng +Rhys Hiltner +Ricardo Padilha Richard Barnes Richard Crowley Richard Eric Gavaletz +Richard Miller Richard Musiol Rick Arnold Rick Hudson +Riku Voipio Risto Jaakko Saarelma Rob Earhart Rob Norman @@ -742,6 +842,7 @@ Rodrigo Moraes de Oliveira Rodrigo Rafael Monti Kochenburger Roger Pau Monné Roger Peppe +Roland Shoemaker Ron Hashimoto Ron Minnich Ross Light @@ -757,17 +858,23 @@ Ryan Seys Ryan Slade S.Çağlar Onur Salmān Aljammāz +Sam Hug Sam Thorogood +Sam Whited Sameer Ajmani +Sami Commerot Sanjay Menakuru Sasha Lionheart Scott Barron +Scott Bell Scott Ferguson Scott Lawrence +Scott Mansfield Scott Schwartz Scott Van Woudenberg Sean Burford Sean Dolphin +Sean Harger Sebastien Binet Sébastien Paolacci Sergei Skorobogatov @@ -775,20 +882,24 @@ Sergey 'SnakE' Gromov Sergey Arseev Sergio Luis O. B. Correia Seth Hoenig +Shahar Kohanim Shane Hansen Shaozhen Ding Shawn Ledbetter Shawn Smith Shawn Walker-Salas Shenghou Ma +Shinji Tanaka Shivakumar GN Shun Fan Silvan Jegen +Simon Jefford Simon Whitehead Sokolov Yura Spencer Nelson Spring Mc Srdjan Petrovic +Sridhar Venkatakrishnan StalkR Stan Schwertly Stefan Nilsson @@ -803,12 +914,14 @@ Steve Streeting Steven Elliot Harris Steven Hartland Sugu Sougoumarane +Suharsh Sivakumar Sven Almgren Szabolcs Nagy Tad Glines Taj Khattra Takashi Matsuo Takeshi YAMANASHI <9.nashi@gmail.com> +Tal Shprecher Tamir Duberstein Tarmigan Casebolt Taru Karttunen @@ -820,13 +933,20 @@ Thomas Desrosiers Thomas Habets Thomas Kappler Thorben Krueger +Tilman Dilo Tim Cooijmans +Tim Ebringer Tim Hockin +Tim Swast Timo Savola Timo Truyts +Timothy Studd +Tipp Moseley Tobias Columbus +Toby Burress Todd Neal Todd Wang +Tom Bergan Tom Heng Tom Linford Tom Szymanski @@ -840,11 +960,13 @@ Trey Tacon Tudor Golubenco Tyler Bunnell Tyler Treat +Tzu-Jung Lee Ugorji Nwoke Ulf Holm Nielsen Ulrich Kunitz Uriel Mangado Uttam C Pawar +Vadim Grek Vadim Vygonets Vega Garcia Luis Alfonso Vincent Ambo @@ -852,9 +974,11 @@ Vincent Batts Vincent Vanackere Vinu Rajashekhar Vish Subramanian +Vishvananda Ishaya Vlad Krasnov Vladimir Nikishenko Volker Dobler +Wedson Almeida Filho Wei Guangjing Will Chan Will Norris @@ -864,6 +988,7 @@ William Josephson William Orr Xia Bin Xing Xing +Xudong Zhang Yan Zou Yann Kerhervé Yao Zhang @@ -879,6 +1004,7 @@ Yusuke Kagiwada Yuusei Kuwana Yuval Pavel Zholkover Yves Junqueira +Zhongwei Yao Ziad Hatahet Zorion Arrizabalaga 申习之 diff --git a/api/next.txt b/api/next.txt index 09e6cf1f965cb2..5ae56c126ada20 100644 --- a/api/next.txt +++ b/api/next.txt @@ -17,7 +17,15 @@ pkg context, type Context interface, Err() error pkg context, type Context interface, Value(interface{}) interface{} pkg context, var Canceled error pkg context, var DeadlineExceeded error +pkg crypto/tls, const RenegotiateFreelyAsClient = 2 +pkg crypto/tls, const RenegotiateFreelyAsClient RenegotiationSupport +pkg crypto/tls, const RenegotiateNever = 0 +pkg crypto/tls, const RenegotiateNever RenegotiationSupport +pkg crypto/tls, const RenegotiateOnceAsClient = 1 +pkg crypto/tls, const RenegotiateOnceAsClient RenegotiationSupport pkg crypto/tls, type Config struct, DynamicRecordSizingDisabled bool +pkg crypto/tls, type Config struct, Renegotiation RenegotiationSupport +pkg crypto/tls, type RenegotiationSupport int pkg crypto/x509, func SystemCertPool() (*CertPool, error) pkg crypto/x509, type SystemRootsError struct, Err error pkg debug/dwarf, method (*Data) Ranges(*Entry) ([][2]uint64, error) @@ -147,8 +155,9 @@ pkg debug/elf, const R_390_TLS_TPOFF R_390 pkg debug/elf, method (R_390) GoString() string pkg debug/elf, method (R_390) String() string pkg debug/elf, type R_390 int -pkg encoding/json, method (*Encoder) DisableHTMLEscaping() -pkg encoding/json, method (*Encoder) Indent(string, string) +pkg encoding/json, method (*Encoder) SetEscapeHTML(bool) +pkg encoding/json, method (*Encoder) SetIndent(string, string) +pkg go/build, type Package struct, BinaryOnly bool pkg go/build, type Package struct, CgoFFLAGS []string pkg go/build, type Package struct, FFiles []string pkg go/doc, type Example struct, Unordered bool @@ -158,22 +167,54 @@ pkg io, const SeekEnd = 2 pkg io, const SeekEnd ideal-int pkg io, const SeekStart = 0 pkg io, const SeekStart ideal-int -pkg io, type SizedReaderAt interface { ReadAt, Size } -pkg io, type SizedReaderAt interface, ReadAt([]uint8, int64) (int, error) -pkg io, type SizedReaderAt interface, Size() int64 pkg math/big, method (*Float) GobDecode([]uint8) error pkg math/big, method (*Float) GobEncode() ([]uint8, error) pkg net, method (*Dialer) DialContext(context.Context, string, string) (Conn, error) -pkg net, type IPNet struct, Zone string pkg net/http, method (*Request) Context() context.Context pkg net/http, method (*Request) WithContext(context.Context) *Request +pkg net/http, type Request struct, Response *Response +pkg net/http, type Response struct, Uncompressed bool pkg net/http, type Transport struct, Dialer *net.Dialer +pkg net/http, type Transport struct, IdleConnTimeout time.Duration +pkg net/http, type Transport struct, MaxIdleConns int pkg net/http, type Transport struct, MaxResponseHeaderBytes int64 +pkg net/http, var ErrUseLastResponse error +pkg net/http, var LocalAddrContextKey *contextKey pkg net/http, var ServerContextKey *contextKey pkg net/http/cgi, type Handler struct, Stderr io.Writer pkg net/http/httptest, func NewRequest(string, string, io.Reader) *http.Request -pkg net/http/httptest, method (*ResponseRecorder) Trailers() http.Header +pkg net/http/httptest, method (*ResponseRecorder) Result() *http.Response +pkg net/http/httptrace, func ContextClientTrace(context.Context) *ClientTrace +pkg net/http/httptrace, func WithClientTrace(context.Context, *ClientTrace) context.Context +pkg net/http/httptrace, type ClientTrace struct +pkg net/http/httptrace, type ClientTrace struct, ConnectDone func(string, string, error) +pkg net/http/httptrace, type ClientTrace struct, ConnectStart func(string, string) +pkg net/http/httptrace, type ClientTrace struct, DNSDone func(DNSDoneInfo) +pkg net/http/httptrace, type ClientTrace struct, DNSStart func(DNSStartInfo) +pkg net/http/httptrace, type ClientTrace struct, GetConn func(string) +pkg net/http/httptrace, type ClientTrace struct, Got100Continue func() +pkg net/http/httptrace, type ClientTrace struct, GotConn func(GotConnInfo) +pkg net/http/httptrace, type ClientTrace struct, GotFirstResponseByte func() +pkg net/http/httptrace, type ClientTrace struct, PutIdleConn func(error) +pkg net/http/httptrace, type ClientTrace struct, Wait100Continue func() +pkg net/http/httptrace, type ClientTrace struct, WroteHeaders func() +pkg net/http/httptrace, type ClientTrace struct, WroteRequest func(WroteRequestInfo) +pkg net/http/httptrace, type DNSDoneInfo struct +pkg net/http/httptrace, type DNSDoneInfo struct, Addrs []net.IPAddr +pkg net/http/httptrace, type DNSDoneInfo struct, Coalesced bool +pkg net/http/httptrace, type DNSDoneInfo struct, Err error +pkg net/http/httptrace, type DNSStartInfo struct +pkg net/http/httptrace, type DNSStartInfo struct, Host string +pkg net/http/httptrace, type GotConnInfo struct +pkg net/http/httptrace, type GotConnInfo struct, Conn net.Conn +pkg net/http/httptrace, type GotConnInfo struct, IdleTime time.Duration +pkg net/http/httptrace, type GotConnInfo struct, Reused bool +pkg net/http/httptrace, type GotConnInfo struct, WasIdle bool +pkg net/http/httptrace, type WroteRequestInfo struct +pkg net/http/httptrace, type WroteRequestInfo struct, Err error pkg net/url, type URL struct, ForceQuery bool +pkg os, method (*File) Size() (int64, error) +pkg os/exec, func CommandContext(context.Context, string, ...string) *Cmd pkg os/user, func LookupGroup(string) (*Group, error) pkg os/user, func LookupGroupId(string) (*Group, error) pkg os/user, method (*User) GroupIds() ([]string, error) @@ -187,6 +228,7 @@ pkg os/user, type UnknownGroupIdError string pkg reflect, func StructOf([]StructField) Type pkg reflect, method (StructTag) Lookup(string) (string, bool) pkg runtime, func CallersFrames([]uintptr) *Frames +pkg runtime, func KeepAlive(interface{}) pkg runtime, func SetCgoTraceback(int, unsafe.Pointer, unsafe.Pointer, unsafe.Pointer) pkg runtime, method (*Frames) Next() (Frame, bool) pkg runtime, type Frame struct @@ -198,6 +240,12 @@ pkg runtime, type Frame struct, Line int pkg runtime, type Frame struct, PC uintptr pkg runtime, type Frames struct pkg strings, method (*Reader) Reset(string) +pkg syscall (linux-386), type SysProcAttr struct, Unshare uintptr +pkg syscall (linux-386-cgo), type SysProcAttr struct, Unshare uintptr +pkg syscall (linux-amd64), type SysProcAttr struct, Unshare uintptr +pkg syscall (linux-amd64-cgo), type SysProcAttr struct, Unshare uintptr +pkg syscall (linux-arm), type SysProcAttr struct, Unshare uintptr +pkg syscall (linux-arm-cgo), type SysProcAttr struct, Unshare uintptr pkg testing, method (*B) Run(string, func(*B)) bool pkg testing, method (*T) Run(string, func(*T)) bool pkg testing, type InternalExample struct, Unordered bool diff --git a/doc/cmd.html b/doc/cmd.html index 5d20d3887ae96c..992f176014e234 100644 --- a/doc/cmd.html +++ b/doc/cmd.html @@ -89,7 +89,7 @@ -vet +vet      Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string. diff --git a/doc/contribute.html b/doc/contribute.html index 1cd6f37d34e950..bcf7b25c511580 100644 --- a/doc/contribute.html +++ b/doc/contribute.html @@ -353,10 +353,13 @@

Make a change

The first line of the change description is conventionally a one-line summary of the change, prefixed by the primary affected package, and is used as the subject for code review mail. -The rest of the -description elaborates and should provide context for the +It should complete the sentence "This change modifies Go to _____." +The rest of the description elaborates and should provide context for the change and explain what it does. +Write in complete sentences with correct punctuation, just like +for your comments in Go. If there is a helpful reference, mention it here. +If you've fixed an issue, reference it by number with a # before it.

@@ -364,7 +367,7 @@

Make a change

-math: improved Sin, Cos and Tan precision for very large arguments
+math: improve Sin, Cos and Tan precision for very large arguments
 
 The existing implementation has poor numerical properties for
 large arguments, so use the McGillicutty algorithm to improve
diff --git a/doc/effective_go.html b/doc/effective_go.html
index 4ea3fae31836e4..1e66c0c6145fae 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -2014,7 +2014,7 @@ 

Pointers vs. Values

type ByteSlice []byte func (slice ByteSlice) Append(data []byte) []byte { - // Body exactly the same as above + // Body exactly the same as the Append function defined above. }

diff --git a/doc/go1.7.html b/doc/go1.7.html new file mode 100644 index 00000000000000..46c575452f7765 --- /dev/null +++ b/doc/go1.7.html @@ -0,0 +1,1132 @@ + + + + + + + + +

+ + +NOTE: This is a DRAFT of the Go 1.7 release notes, prepared for the Go 1.7 beta. +Go 1.7 has NOT yet been released. +By our regular schedule, it is expected some time in August 2016. + +

+ +

Introduction to Go 1.7

+ +

+The latest Go release, version 1.7, arrives six months after 1.6. +Most of its changes are in the implementation of the toolchain, runtime, and libraries. +There is one minor change to the language specification. +As always, the release maintains the Go 1 promise of compatibility. +We expect almost all Go programs to continue to compile and run as before. +

+ +

+The release adds a port to IBM LinuxOne; +updates the x86-64 compiler back end to generate more efficient code; +includes the context package, promoted from the +x/net subrepository +and now used in the standard library; +and adds support in the testing package for +creating hierarchies of tests and benchmarks. +The release also finalizes the vendoring support +started in Go 1.5, making it a standard feature. +

+ +

Changes to the language

+ +

+There is one tiny language change in this release. +The section on terminating statements +clarifies that to determine whether a statement list ends in a terminating statement, +the “final non-empty statement” is considered the end, +matching the existing behavior of the gc and gccgo compiler toolchains. +In earlier releases the definition referred only to the “final statement,” +leaving the effect of trailing empty statements at the least unclear. +The go/types +package has been updated to match the gc and gccgo compiler toolchains +in this respect. +This change has no effect on the correctness of existing programs. +

+ +

Ports

+ +

+Go 1.7 adds an experimental port to Linux on z Systems (linux/s390x) +and the beginning of a port to Plan 9 on ARM (plan9/arm). +

+ +

+The experimental ports to Linux on 64-bit MIPS (linux/mips64 and linux/mips64le) +added in Go 1.6 now have full support for cgo and external linking. +

+ +

+The experimental port to Linux on big-endian 64-bit PowerPC (linux/ppc64) +now requires the POWER8 architecture or later. +

+ +

+The OpenBSD port now requires OpenBSD 5.6 or later, for access to the getentropy(2) system call. +

+ +

Tools

+ +

Assembler

+ +

+For 64-bit ARM systems, the vector register names have been +corrected to V0 through V31; +previous releases incorrectly referred to them as V32 through V63. +

+ +

+For 64-bit x86 systems, the following instructions have been added: +PCMPESTRI, +RORXL, +RORXQ, +VINSERTI128, +VPADDD, +VPADDQ, +VPALIGNR, +VPBLENDD, +VPERM2F128, +VPERM2I128, +VPOR, +VPSHUFB, +VPSHUFD, +VPSLLD, +VPSLLDQ, +VPSLLQ, +VPSRLD, +VPSRLDQ, +and +VPSRLQ. +

+ +

Compiler Toolchain

+ +

+This release includes a new code generation back end for 64-bit x86 systems, +following a proposal from 2015 +that has been under development since then. +The new back end, based on +SSA, +generates more compact, more efficient code +and provides a better platform for optimizations +such as bounds check elimination. +The new back end reduces the CPU time required by +our benchmark programs by 5-35%. +

+ +

+For this release, the new back end can be disabled by passing +-ssa=0 to the compiler. +If you find that your program compiles or runs successfully +only with the new back end disabled, please +file a bug report. +

+ +

+The format of exported metadata written by the compiler in package archives has changed: +the old textual format has been replaced by a more compact binary format. +This results in somewhat smaller package archives and fixes a few +long-standing corner case bugs. +

+ +

+For this release, the new export format can be disabled by passing +-newexport=0 to the compiler. +If you find that your program compiles or runs successfully +only with the new export format disabled, please +file a bug report. +

+ +

+The linker's -X option no longer supports the unusual two-argument form +-X name value, +as announced in the Go 1.6 release +and in warnings printed by the linker. +Use -X name=value instead. +

+ +

+The compiler and linker have been optimized and run significantly faster in this release than in Go 1.6, +although they are still slower than we would like and will continue to be optimized in future releases. +

+ +

+Due to changes across the compiler toolchain and standard library, +binaries built with this release should typically be smaller than binaries +built with Go 1.6, +sometimes by as much as 20-30%. +

+ +

Cgo

+ +

+Packages using cgo may now include +Fortran source files (in addition to C, C++, Objective C, and SWIG), +although the Go bindings must still use C language APIs. +

+ +

+Go bindings may now use a new helper function C.CBytes. +In contrast to C.CString, which takes a Go string +and returns a *C.byte (a C char*), +C.CBytes takes a Go []byte +and returns an unsafe.Pointer (a C void*). +

+ +

+Packages and binaries built using cgo have in past releases +produced different output on each build, +due to the embedding of temporary directory names. +When using this release with +new enough versions of GCC or Clang +(those that support the -fdebug-prefix-map option), +those builds should finally be deterministic. +

+ +

Gccgo

+ +

+Due to the alignment of Go's semiannual release schedule with GCC's annual release schedule, +GCC release 6 contains the Go 1.6.1 version of gccgo. +The next release, GCC 7, will likely have the Go 1.8 version of gccgo. +

+ +

Go command

+ +

+The go command's basic operation +is unchanged, but there are a number of changes worth noting. +

+ +

+This release removes support for the GO15VENDOREXPERIMENT environment variable, +as announced in the Go 1.6 release. +Vendoring support +is now a standard feature of the go command and toolchain. +

+ +

+The Package data structure made available to +“go list” now includes a +StaleReason field explaining why a particular package +is or is not considered stale (in need of rebuilding). +This field is available to the -f or -json +options and is useful for understanding why a target is being rebuilt. +

+ +

+The “go get” command now supports +import paths referring to git.openstack.org. +

+ +

+This release adds experimental, minimal support for building programs using +binary-only packages, +packages distributed in binary form +without the corresponding source code. +This feature is needed in some commercial settings +but is not intended to be fully integrated into the rest of the toolchain. +For example, tools that assume access to complete source code +will not work with such packages, and there are no plans to support +such packages in the “go get” command. +

+ +

Go doc

+ +

+The “go doc” command +now groups constructors with the type they construct, +following godoc. +

+ +

Go vet

+ +

+The “go vet” command +has more accurate analysis in its -copylock and -printf checks, +and a new -tests check that checks the name and signature of likely test functions. +To avoid confusion with the new -tests check, the old, unadvertised +-test option has been removed; it was equivalent to -all -shadow. +

+ +

Go tool dist

+ +

+The new subcommand “go tool dist list” +prints all supported operating system/architecture pairs. +

+ +

Go tool trace

+ +

+The “go tool trace” command, +introduced in Go 1.5, +has been refined in various ways. +

+ +

+First, collecting traces is significantly more efficient than in past releases. +In this release, the typical execution-time overhead of collecting a trace is about 25%; +in past releases it was at least 400%. +Second, trace files now include file and line number information, +making them more self-contained and making the +original executable optional when running the trace tool. +Third, the trace tool now breaks up large traces to avoid limits +in the browser-based viewer. +

+ +

+Although the trace file format has changed in this release, +the Go 1.7 tools can still read traces from earlier releases. +

+ +

Performance

+ +

+As always, the changes are so general and varied that precise statements +about performance are difficult to make. +Most programs should run a bit faster, +due to speedups in the garbage collector and +optimizations in the core library. +On x86-64 systems, many programs will run significantly faster, +due to improvements in generated code brought by the +new compiler back end. +As noted above, in our own benchmarks, +the code generation changes alone typically reduce program CPU time by 5-35%. +

+ +

+ +There have been significant optimizations bringing more than 10% improvements +to implementations in the +crypto/sha1, +crypto/sha256, +encoding/binary, +fmt, +hash/adler32, +hash/crc32, +hash/crc64, +image/color, +math/big, +strconv, +strings, +unicode, +and +unicode/utf16 +packages. +

+ +

Core library

+ +

Context

+ +

+Go 1.7 moves the golang.org/x/net/context package +into the standard library as context. +This allows the use of contexts for cancellation, timeouts, and passing +request-scoped data in other standard library packages, +including +net, +net/http, +and +os/exec, +as noted below. +

+ +

+For more information about contexts, see the +package documentation +and the Go blog post +“Go Concurrent Patterns: Context.” +

+ +

Testing

+ +

+The testing package now supports the definition +of tests with subtests and benchmarks with sub-benchmarks. +This support makes it easy to write table-driven benchmarks +and to create hierarchical tests. +It also provides a way to share common setup and tear-down code. +See the package documentation for details. +

+ +

Runtime

+ +

+All panics started by the runtime now use panic values +that implement both the builtin error, +and +runtime.Error, +as +required by the language specification. +

+ +

+During panics, if a signal's name is known, it will be printed in the stack trace. +Otherwise, the signal's number will be used, as it was before Go1.7. +

+ +

+The new function +KeepAlive +provides an explicit mechanism for declaring +that an allocated object must be considered reachable +at a particular point in a program, +typically to delay the execution of an associated finalizer. +

+ +

+The new function +CallersFrames +translates a PC slice obtained from +Callers +into a sequence of frames corresponding to the call stack. +This new API should be preferred instead of direct use of +FuncForPC, +because the frame sequence can more accurately describe +call stacks with inlined function calls. +

+ +

+The new function +SetCgoTraceback +facilitates tighter integration between Go and C code executing +in the same process called using cgo. +

+ +

+On 32-bit systems, the runtime can now use memory allocated +by the operating system anywhere in the address space, +eliminating the +“memory allocated by OS not in usable range” failure +common in some environments. +

+ +

+On Windows, Go programs in Go 1.5 and earlier forced +the global Windows timer resolution to 1ms at startup +by calling timeBeginPeriod(1). +Changing the global timer resolution caused problems on some systems, +and testing suggested that the call was not needed for good scheduler performance, +so Go 1.6 removed the call. +Go 1.7 brings the call back: under some workloads the call +is still needed for good scheduler performance. +

+ + +

Minor changes to the library

+ +

+As always, there are various minor changes and updates to the library, +made with the Go 1 promise of compatibility +in mind. +

+ +
bufio
+ +
+

+In previous releases of Go, if +Reader's +Peek method +were asked for more bytes than fit in the underlying buffer, +it would return an empty slice and the error ErrBufferFull. +Now it returns the entire underlying buffer, still accompanied by the error ErrBufferFull. +

+
+ +
bytes
+ +
+

+The new functions +ContainsAny and +ContainsRune +have been added for symmetry with +the strings package. +

+ +

+In previous releases of Go, if +Reader's +Read method +were asked for zero bytes with no data remaining, it would +return a count of 0 and no error. +Now it returns a count of 0 and the error +io.EOF . +

+ +

+The +Reader type has a new method +Reset to allow reuse of a Reader. +

+
+ +
compress/flate
+ +
+

+As noted above, +there are significant performance optimizations throughout the package. +Decompression speed is improved by about 10%, +while compression speed for DefaultCompression is roughly doubled. +

+ +

+In addition to those general improvements, +the +BestSpeed +compressor has been replaced entirely and uses an +algorithm similar to Snappy, +resulting in about a 2.5X speed increase, +although the output can be 5-10% larger than with the previous algorithm. +

+ +

+There is also a new compression level +HuffmanOnly +that applies Huffman but not Lempel-Ziv encoding. +Forgoing Lempel-Ziv encoding means that +HuffmanOnly runs about 3X faster than the new BestSpeed +but at the cost of producing compressed outputs that are 20-40% larger than those +generated by the new BestSpeed. +

+
+ +
crypto/tls
+ +
+

+The TLS implementation sends the first few data packets on each connection +using small record sizes, gradually increasing to the TLS maximum record size. +This heuristic reduces the amount of data that must be received before +the first packet can be decrypted, improving communication latency over +low-bandwidth networks. +Setting +Config's +DynamicRecordSizingDisabled field to true +forces the behavior of Go 1.6 and earlier, where packets are +as large as possible from the start of the connection. +

+ +

+The TLS client now has optional, limited support for server-initiated renegotiation, +enabled by setting the +Config's +Renegotiation field. +This is needed for connecting to many Microsoft Azure servers. +

+ +

+The errors returned by the package now consistently begin with a +tls: prefix. +In past releases, some errors used a crypto/tls: prefix, +some used a tls: prefix, and some had no prefix at all. +

+ +

+When generating self-signed certificates, the package no longer sets the +“Authority Key Identifier” field by default. +

+
+ +
crypto/x509
+ +
+

+The new function +SystemCertPool +provides access to the entire system certificate pool if available. +There is also a new associated error type +SystemRootsError. +

+
+ +
debug/dwarf
+ +
+

+The +Reader type's new +SeekPC method and the +Data type's new +Ranges method +help to find the compilation unit to pass to a +LineReader +and to identify the specific function for a given program counter. +

+
+ +
debug/elf
+ +
+

+The new +R_390 relocation type +and its many predefined constants +support the S390 port. +

+
+ +
encoding/asn1
+ +
+

+The ASN.1 decoder now rejects non-minimal integer encodings. +This may cause the package to reject some invalid but formerly accepted ASN.1 data. +

+
+ +
encoding/json
+ +
+

+The +Encoder's new +SetIndent method +sets the indentation parameters for JSON encoding, +like in the top-level +Indent function. +

+ +

+The +Encoder's new +SetEscapeHTML method +controls whether the +&, <, and > +characters in quoted strings should be escaped as +\u0026, \u003c, and \u003e, +respectively. +As in previous releases, the encoder defaults to applying this escaping, +to avoid certain problems that can arise when embedding JSON in HTML. +

+ +

+In earlier versions of Go, this package only supported encoding and decoding +maps using keys with string types. +Go 1.7 adds support for maps using keys with integer types: +the encoding uses a quoted decimal representation as the JSON key. +Go 1.7 also adds support for encoding maps using non-string keys that implement +MarshalJSON +(see +Marshaler) +or +MarshalText +(see +encoding.TextMarshaler) +methods, +as well as support for decoding maps using non-string keys that implement +UnmarshalJSON +(see +Unmarshaler) +or +UnmarshalText +(see +encoding.TextUnmarshaler) +methods. +These methods are ignored for keys with string types in order to preserve +the encoding and decoding used in earlier versions of Go. +

+ +

+When encoding a slice of typed bytes, +Marshal +now generates an array of elements encoded using +that byte type's +MarshalJSON +or +MarshalText +method if present, +only falling back to the default base64-encoded string data if neither method is available. +Earlier versions of Go accept both the original base64-encoded string encoding +and the array encoding (assuming the byte type also implements +UnmarshalJSON +or +UnmarshalText +as appropriate), +so this change should be semantically backwards compatible with earlier versions of Go, +even though it does change the chosen encoding. +

+
+ +
go/build
+ +
+

+To implement the go command's new support for binary-only packages +and for Fortran code in cgo-based packages, +the +Package type +adds new fields BinaryOnly, CgoFFLAGS, and FFiles. +

+
+ +
go/doc
+ +
+

+To support the corresponding change in go test described above, +Example struct adds a Unordered field +indicating whether the example may generate its output lines in any order. +

+
+ +
io
+ +
+

+The package adds new constants +SeekStart, SeekCurrent, and SeekEnd, +for use with +Seeker +implementations. +These constants are preferred over os.SEEK_SET, os.SEEK_CUR, and os.SEEK_END, +but the latter will be preserved for compatibility. +

+
+ +
math/big
+ +
+

+The +Float type adds +GobEncode and +GobDecode methods, +so that values of type Float can now be encoded and decoded using the +encoding/gob +package. +

+
+ +
mime/multipart
+ +
+

+The +Writer +implementation now emits each multipart section's header sorted by key. +Previously, iteration over a map caused the section header to use a +non-deterministic order. +

+
+ +
net
+ +
+

+As part of the introduction of context, the +Dialer type has a new method +DialContext, like +Dial but adding the +context.Context +for the dial operation. +The context is intended to obsolete the Dialer's +Cancel and Deadline fields, +but the implementation continues to respect them, +for backwards compatibility. +

+ +

+The +IP type's +String method has changed its result for invalid IP addresses. +In past releases, if an IP byte slice had length other than 0, 4, or 16, String +returned "?". +Go 1.7 adds the hexadecimal encoding of the bytes, as in "?12ab". +

+ +

+The pure Go name resolution +implementation now respects nsswtch.conf's +stated preference for the priority of DNS lookups compared to +local file (that is, /etc/hosts) lookups. +

+
+ +
net/http
+ +
+

+ResponseWriter's +documentation now makes clear that beginning to write the response +may prevent future reads on the request body. +For maximal compatibility, implementations are encouraged to +read the request body completely before writing any part of the response. +

+ +

+As part of the introduction of context, the +Request has a new methods +Context, to retrieve the associated context, and +WithContext, to construct a copy of Request +with a modified context. +

+ +

+In the +Server implementation, +Serve records in the request context +both the underlying *Server using the key ServerContextKey +and the local address on which the request was received (a +Addr) using the key LocalAddrContextKey. +For example, the address on which a request received is +req.Context().Value(http.LocalAddrContextKey).(net.Addr). +

+ +

+The server implementation now +pads response codes less than 100 to three digits +as required by the protocol, +so that w.WriteHeader(5) uses the HTTP response +status 005, not just 5. +

+ +

+In the client, the +Transport implementation passes the request context +to any dial operation connecting to the remote server. +If a custom dialer is needed, the new Transport field +DialContext is preferred over the existing Dial field, +to allow the transport to supply a context. +

+ +

+The +Transport also adds fields +IdleConnTimeout, +MaxIdleConns, +and +MaxResponseHeaderBytes +to help control client resources consumed +by idle or chatty servers. +

+ +

+A +Client's configured CheckRedirect function can now +return ErrUseLastResponse to indicate that the +most recent redirect response should be returned as the +result of the HTTP request. +That response is now available to the CheckRedirect function +as req.Response. +

+ +

+Since Go 1, the default behavior of the HTTP client is +to request server-side compression +using the Accept-Encoding request header +and then to uncompress the response body transparently, +and this behavior is adjustable using the +Transport's DisableCompression field. +In Go 1.7, to aid the implementation of HTTP proxies, the +Response's new +Uncompressed field reports whether +this transparent uncompression took place. +

+ +

+DetectContentType +adds support for a few new audio and video content types. +

+
+ +
net/http/cgi
+ +
+

+The +Handler +adds a new field +Stderr +that allows redirection of the child process's +standard error away from the host process's +standard error. +

+
+ +
net/http/httptest
+ +
+

+The new function +NewRequest +prepares a new +http.Request +suitable for passing to an +http.Handler during a test. +

+ +

+The +ResponseRecorder's new +Result method +returns the recorded +http.Response. +Tests that need to check the response's headers or trailers +should call Result and inspect the response fields +instead of accessing +ResponseRecorder's HeaderMap directly. +

+
+ +
net/http/httputil
+ +
+

+The +ReverseProxy implementation now responds with “502 Bad Gateway” +when it cannot reach a back end; in earlier releases it responded with “500 Internal Server Error.” +

+ +

+Both +ClientConn and +ServerConn have been documented as deprecated. +They are low-level, old, and unused by Go's current HTTP stack +and will no longer be updated. +Programs should use +http.Client, +http.Transport, +and +http.Server +instead. +

+
+ +
net/http/pprof
+ +
+

+The runtime trace HTTP handler, installed to handle the path /debug/pprof/trace, +now accepts a fractional number in its seconds query parameter, +allowing collection of traces for intervals smaller than one second. +This is especially useful on busy servers. +

+
+ +
net/mail
+ +
+

+The address parser now allows unescaped UTF-8 text in addresses +following RFC 6532, +but it does not apply any normalization to the result. +For compatibility with older mail parsers, +the address encoder, namely +Address's +String method, +continues to escape all UTF-8 text following RFC 5322, +

+
+ +
net/url
+ +
+

+The +URL's +new ForceQuery field +records whether the URL must have a query string, +in order to distinguish URLs without query strings (like /search) +from URLs with empty query strings (like /search?). +

+
+ +
os
+ +
+

+The +File +type adds a new +Size +method, so that File implements the new +SizedReaderAt method. +

+ +

+IsExists now returns true for syscall.ENOTEMPTY, +on systems where that error exists. +

+ +

+On Windows, +Remove now removes read-only files when possible, +making the implementation behave as on +non-Windows systems. +

+
+ +
os/exec
+ +
+

+As part of the introduction of context, +the new constructor +CommandContext +is like +Command but includes a context that can be used to cancel the command execution. +

+
+ +
os/user
+ +
+

+The +Current +function is now implemented even when cgo is not available. +

+ +

+The new +Group type, +along with the lookup functions +LookupGroup and +LookupGroupId +and the new field GroupIds in the User struct, +provides access to system-specific user group information. +

+
+ +
reflect
+ +
+

+Although +Value's +Field method has always been documented to panic +if the given field number i is out of range, it has instead +silently returned a zero +Value. +Go 1.7 changes the method to behave as documented. +

+ +

+The new +StructOf +function constructs a struct type at run time. +It completes the set of type constructors, joining +ArrayOf, +ChanOf, +FuncOf, +MapOf, +PtrTo, +and +SliceOf. +

+ +

+StructTag's +new method +Lookup +is like +Get +but distinguishes the tag not containing the given key +from the tag associating an empty string with the given key. +

+ +

+The +Method and +NumMethod +methods of +Type and +Value +no longer return or count unexported methods. +

+
+ +
strings
+ +
+

+In previous releases of Go, if +Reader's +Read method +were asked for zero bytes with no data remaining, it would +return a count of 0 and no error. +Now it returns a count of 0 and the error +io.EOF . +

+ +

+The +Reader type has a new method +Reset to allow reuse of a Reader. +

+
+ +
time
+ +
+

+Duration's +time.Duration.String method now reports the zero duration as "0s", not "0". +ParseDuration continues to accept both forms. +

+ +

+The method call time.Local.String() now returns "Local" on all systems; +in earlier releases, it returned an empty string on Windows. +

+ +

+The time zone database in +$GOROOT/lib/time has been updated +to IANA release 2016d. +This fallback database is only used when the system time zone database +cannot be found, for example on Windows. +The Windows time zone abbreviation list has also been updated. +

+
+ +
syscall
+ +
+

+On Linux, the +SysProcAttr struct +(as used in +os/exec.Cmd's SysProcAttr field) +has a new Unshare field. +If the field is nonzero, the child process created by +ForkExec +(as used in exec.Cmd's Run method) +will call the +unshare(2) +system call before executing the new program. +

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

Introduction

-The Go compilers support five instruction sets. +The Go compilers support six instruction sets. There are important differences in the quality of the compilers for the different architectures.

@@ -63,19 +63,19 @@

Introduction

arm64 (AArch64)
- Supports Linux and Darwin binaries. New in 1.5 and not as well excercised as other ports. + Supports Linux and Darwin binaries. New in 1.5 and not as well exercised as other ports.
ppc64, ppc64le (64-bit PowerPC big- and little-endian)
- Supports Linux binaries. New in 1.5 and not as well excercised as other ports. + Supports Linux binaries. New in 1.5 and not as well exercised as other ports.
mips64, mips64le (64-bit MIPS big- and little-endian)
- Supports Linux binaries. New in 1.6 and not as well excercised as other ports. + Supports Linux binaries. New in 1.6 and not as well exercised as other ports.
diff --git a/doc/install.html b/doc/install.html index 96a7672778bb69..0e6b86fdaf5d15 100644 --- a/doc/install.html +++ b/doc/install.html @@ -221,7 +221,7 @@

Test your installation

Create a directory to contain your workspace, $HOME/work - + for example, and set the GOPATH environment variable to point to that location.

@@ -231,7 +231,7 @@

Test your installation

diff --git a/misc/cgo/errors/issue14669.go b/misc/cgo/errors/issue14669.go new file mode 100644 index 00000000000000..04d2bcb631d249 --- /dev/null +++ b/misc/cgo/errors/issue14669.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 14669: test that fails when build with CGO_CFLAGS selecting +// optimization. + +package p + +/* +const int E = 1; + +typedef struct s { + int c; +} s; +*/ +import "C" + +func F() { + _ = C.s{ + c: C.E, + } +} diff --git a/misc/cgo/errors/ptr.go b/misc/cgo/errors/ptr.go index b6cec8e10d0fe2..27eb78e36cfbc0 100644 --- a/misc/cgo/errors/ptr.go +++ b/misc/cgo/errors/ptr.go @@ -290,6 +290,30 @@ var ptrTests = []ptrTest{ }, fail: true, }, + { + // Don't check non-pointer data. + // Uses unsafe code to get a pointer we shouldn't check. + // Although we use unsafe, the uintptr represents an integer + // that happens to have the same representation as a pointer; + // that is, we are testing something that is not unsafe. + name: "ptrdata1", + c: `#include + void f(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S struct { p *int; a [8*8]byte; u uintptr }`, + body: `i := 0; p := &S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f(unsafe.Pointer(q))`, + fail: false, + }, + { + // Like ptrdata1, but with a type that uses a GC program. + name: "ptrdata2", + c: `#include + void f(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`, + body: `i := 0; p := S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f(unsafe.Pointer(q))`, + fail: false, + }, } func main() { diff --git a/misc/cgo/errors/test.bash b/misc/cgo/errors/test.bash index cd358a10f815e0..643d0382050285 100755 --- a/misc/cgo/errors/test.bash +++ b/misc/cgo/errors/test.bash @@ -45,6 +45,13 @@ expect issue13129.go C.ushort check issue13423.go expect issue13635.go C.uchar C.schar C.ushort C.uint C.ulong C.longlong C.ulonglong C.complexfloat C.complexdouble +if ! go build issue14669.go; then + exit 1 +fi +if ! CGO_CFLAGS="-O" go build issue14669.go; then + exit 1 +fi + if ! go run ptr.go; then exit 1 fi diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go index c0c45398cda9db..ab14c007a9cb52 100644 --- a/misc/cgo/testcarchive/carchive_test.go +++ b/misc/cgo/testcarchive/carchive_test.go @@ -79,14 +79,12 @@ func init() { } if GOOS == "darwin" { - cc = append(cc, "-Wl,-no_pie") - // For Darwin/ARM. // TODO(crawshaw): can we do better? cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) } libgodir = GOOS + "_" + GOARCH - if GOOS == "darwin" && GOARCH == "arm" { + if GOOS == "darwin" && (GOARCH == "arm" || GOARCH == "arm64") { libgodir = GOOS + "_" + GOARCH + "_shared" } cc = append(cc, "-I", filepath.Join("pkg", libgodir)) diff --git a/misc/cgo/testcarchive/main4.c b/misc/cgo/testcarchive/main4.c index 2aaf09b7c1961e..353f980c50d47e 100644 --- a/misc/cgo/testcarchive/main4.c +++ b/misc/cgo/testcarchive/main4.c @@ -44,8 +44,7 @@ static void init() { // Test raising SIGIO on a C thread with an alternate signal stack // when there is a Go signal handler for SIGIO. -static void* thread1(void* arg) { - pthread_t* ptid = (pthread_t*)(arg); +static void* thread1(void* arg __attribute__ ((unused))) { stack_t ss; int i; stack_t nss; @@ -65,7 +64,7 @@ static void* thread1(void* arg) { // Send ourselves a SIGIO. This will be caught by the Go // signal handler which should forward to the C signal // handler. - i = pthread_kill(*ptid, SIGIO); + i = pthread_kill(pthread_self(), SIGIO); if (i != 0) { fprintf(stderr, "pthread_kill: %s\n", strerror(i)); exit(EXIT_FAILURE); @@ -101,11 +100,11 @@ static void* thread1(void* arg) { // Test calling a Go function to raise SIGIO on a C thread with an // alternate signal stack when there is a Go signal handler for SIGIO. -static void* thread2(void* arg) { - pthread_t* ptid = (pthread_t*)(arg); +static void* thread2(void* arg __attribute__ ((unused))) { stack_t ss; int i; int oldcount; + pthread_t tid; stack_t nss; // Set up an alternate signal stack for this thread. @@ -124,7 +123,8 @@ static void* thread2(void* arg) { // Call a Go function that will call a C function to send us a // SIGIO. - GoRaiseSIGIO(ptid); + tid = pthread_self(); + GoRaiseSIGIO(&tid); // Wait until the signal has been delivered. i = 0; @@ -161,7 +161,7 @@ int main(int argc, char **argv) { // Tell the Go library to start looking for SIGIO. GoCatchSIGIO(); - i = pthread_create(&tid, NULL, thread1, (void*)(&tid)); + i = pthread_create(&tid, NULL, thread1, NULL); if (i != 0) { fprintf(stderr, "pthread_create: %s\n", strerror(i)); exit(EXIT_FAILURE); @@ -173,7 +173,7 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } - i = pthread_create(&tid, NULL, thread2, (void*)(&tid)); + i = pthread_create(&tid, NULL, thread2, NULL); if (i != 0) { fprintf(stderr, "pthread_create: %s\n", strerror(i)); exit(EXIT_FAILURE); diff --git a/misc/cgo/testsanitizers/test.bash b/misc/cgo/testsanitizers/test.bash index 76628abaff8741..c30df3b6c2a5ec 100755 --- a/misc/cgo/testsanitizers/test.bash +++ b/misc/cgo/testsanitizers/test.bash @@ -134,6 +134,26 @@ if test "$tsan" = "yes"; then status=1 fi + if ! go run tsan3.go 2>$err; then + cat $err + echo "FAIL: tsan3" + status=1 + elif grep -i warning $err >/dev/null 2>&1; then + cat $err + echo "FAIL: tsan3" + status=1 + fi + + if ! go run tsan4.go 2>$err; then + cat $err + echo "FAIL: tsan4" + status=1 + elif grep -i warning $err >/dev/null 2>&1; then + cat $err + echo "FAIL: tsan4" + status=1 + fi + rm -f $err fi diff --git a/misc/cgo/testsanitizers/tsan3.go b/misc/cgo/testsanitizers/tsan3.go new file mode 100644 index 00000000000000..87f6c80f1b18d5 --- /dev/null +++ b/misc/cgo/testsanitizers/tsan3.go @@ -0,0 +1,40 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// The stubs for the C functions read and write the same slot on the +// g0 stack when copying arguments in and out. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +int Func1() { + return 0; +} + +void Func2(int x) { + (void)x; +} +*/ +import "C" + +func main() { + const N = 10000 + done := make(chan bool, N) + for i := 0; i < N; i++ { + go func() { + C.Func1() + done <- true + }() + go func() { + C.Func2(0) + done <- true + }() + } + for i := 0; i < 2*N; i++ { + <-done + } +} diff --git a/misc/cgo/testsanitizers/tsan4.go b/misc/cgo/testsanitizers/tsan4.go new file mode 100644 index 00000000000000..f0c76d84116a60 --- /dev/null +++ b/misc/cgo/testsanitizers/tsan4.go @@ -0,0 +1,34 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Check that calls to C.malloc/C.free do not trigger TSAN false +// positive reports. + +// #cgo CFLAGS: -fsanitize=thread +// #cgo LDFLAGS: -fsanitize=thread +// #include +import "C" + +import ( + "runtime" + "sync" +) + +func main() { + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + p := C.malloc(C.size_t(i * 10)) + runtime.Gosched() + C.free(p) + } + }() + } + wg.Wait() +} diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto index 8c14b87f0a5299..8a8784c8be011c 100644 --- a/misc/nacl/testzip.proto +++ b/misc/nacl/testzip.proto @@ -27,23 +27,23 @@ go src=.. internal objfile objfile.go - unvendor - golang.org - x - arch - arm - armasm - testdata - + - x86 - x86asm - testdata - + gofmt gofmt.go gofmt_test.go testdata + + vendor + golang.org + x + arch + arm + armasm + testdata + + + x86 + x86asm + testdata + + archive tar testdata diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go index 36f4e23980930c..2a1e4321826195 100644 --- a/src/archive/tar/common.go +++ b/src/archive/tar/common.go @@ -21,10 +21,8 @@ import ( "time" ) +// Header type flags. const ( - blockSize = 512 - - // Types TypeReg = '0' // regular file TypeRegA = '\x00' // regular file TypeLink = '1' // hard link @@ -61,12 +59,6 @@ type Header struct { Xattrs map[string]string } -// File name constants from the tar spec. -const ( - fileNameSize = 100 // Maximum number of bytes in a standard tar name. - fileNamePrefixSize = 155 // Maximum number of ustar extension bytes. -) - // FileInfo returns an os.FileInfo for the Header. func (h *Header) FileInfo() os.FileInfo { return headerFileInfo{h} @@ -279,33 +271,6 @@ func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { return h, nil } -var zeroBlock = make([]byte, blockSize) - -// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values. -// We compute and return both. -func checksum(header []byte) (unsigned int64, signed int64) { - for i := 0; i < len(header); i++ { - if i == 148 { - // The chksum field (header[148:156]) is special: it should be treated as space bytes. - unsigned += ' ' * 8 - signed += ' ' * 8 - i += 7 - continue - } - unsigned += int64(header[i]) - signed += int64(int8(header[i])) - } - return -} - -type slicer []byte - -func (sp *slicer) next(n int) (b []byte) { - s := *sp - b, *sp = s[0:n], s[n:] - return -} - func isASCII(s string) bool { for _, c := range s { if c >= 0x80 { diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go new file mode 100644 index 00000000000000..c2c9910d00281f --- /dev/null +++ b/src/archive/tar/format.go @@ -0,0 +1,197 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +// Constants to identify various tar formats. +const ( + // The format is unknown. + formatUnknown = (1 << iota) / 2 // Sequence of 0, 1, 2, 4, 8, etc... + + // The format of the original Unix V7 tar tool prior to standardization. + formatV7 + + // The old and new GNU formats, which are incompatible with USTAR. + // This does cover the old GNU sparse extension. + // This does not cover the GNU sparse extensions using PAX headers, + // versions 0.0, 0.1, and 1.0; these fall under the PAX format. + formatGNU + + // Schily's tar format, which is incompatible with USTAR. + // This does not cover STAR extensions to the PAX format; these fall under + // the PAX format. + formatSTAR + + // USTAR is the former standardization of tar defined in POSIX.1-1988. + // This is incompatible with the GNU and STAR formats. + formatUSTAR + + // PAX is the latest standardization of tar defined in POSIX.1-2001. + // This is an extension of USTAR and is "backwards compatible" with it. + // + // Some newer formats add their own extensions to PAX, such as GNU sparse + // files and SCHILY extended attributes. Since they are backwards compatible + // with PAX, they will be labelled as "PAX". + formatPAX +) + +// Magics used to identify various formats. +const ( + magicGNU, versionGNU = "ustar ", " \x00" + magicUSTAR, versionUSTAR = "ustar\x00", "00" + trailerSTAR = "tar\x00" +) + +// Size constants from various tar specifications. +const ( + blockSize = 512 // Size of each block in a tar stream + nameSize = 100 // Max length of the name field in USTAR format + prefixSize = 155 // Max length of the prefix field in USTAR format +) + +var zeroBlock block + +type block [blockSize]byte + +// Convert block to any number of formats. +func (b *block) V7() *headerV7 { return (*headerV7)(b) } +func (b *block) GNU() *headerGNU { return (*headerGNU)(b) } +func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) } +func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) } +func (b *block) Sparse() sparseArray { return (sparseArray)(b[:]) } + +// GetFormat checks that the block is a valid tar header based on the checksum. +// It then attempts to guess the specific format based on magic values. +// If the checksum fails, then formatUnknown is returned. +func (b *block) GetFormat() (format int) { + // Verify checksum. + var p parser + value := p.parseOctal(b.V7().Chksum()) + chksum1, chksum2 := b.ComputeChecksum() + if p.err != nil || (value != chksum1 && value != chksum2) { + return formatUnknown + } + + // Guess the magic values. + magic := string(b.USTAR().Magic()) + version := string(b.USTAR().Version()) + trailer := string(b.STAR().Trailer()) + switch { + case magic == magicUSTAR && trailer == trailerSTAR: + return formatSTAR + case magic == magicUSTAR: + return formatUSTAR + case magic == magicGNU && version == versionGNU: + return formatGNU + default: + return formatV7 + } +} + +// SetFormat writes the magic values necessary for specified format +// and then updates the checksum accordingly. +func (b *block) SetFormat(format int) { + // Set the magic values. + switch format { + case formatV7: + // Do nothing. + case formatGNU: + copy(b.GNU().Magic(), magicGNU) + copy(b.GNU().Version(), versionGNU) + case formatSTAR: + copy(b.STAR().Magic(), magicUSTAR) + copy(b.STAR().Version(), versionUSTAR) + copy(b.STAR().Trailer(), trailerSTAR) + case formatUSTAR, formatPAX: + copy(b.USTAR().Magic(), magicUSTAR) + copy(b.USTAR().Version(), versionUSTAR) + default: + panic("invalid format") + } + + // Update checksum. + // This field is special in that it is terminated by a NULL then space. + var f formatter + field := b.V7().Chksum() + chksum, _ := b.ComputeChecksum() // Possible values are 256..128776 + f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143 + field[7] = ' ' +} + +// ComputeChecksum computes the checksum for the header block. +// POSIX specifies a sum of the unsigned byte values, but the Sun tar used +// signed byte values. +// We compute and return both. +func (b *block) ComputeChecksum() (unsigned, signed int64) { + for i, c := range b { + if 148 <= i && i < 156 { + c = ' ' // Treat the checksum field itself as all spaces. + } + unsigned += int64(uint8(c)) + signed += int64(int8(c)) + } + return unsigned, signed +} + +type headerV7 [blockSize]byte + +func (h *headerV7) Name() []byte { return h[000:][:100] } +func (h *headerV7) Mode() []byte { return h[100:][:8] } +func (h *headerV7) UID() []byte { return h[108:][:8] } +func (h *headerV7) GID() []byte { return h[116:][:8] } +func (h *headerV7) Size() []byte { return h[124:][:12] } +func (h *headerV7) ModTime() []byte { return h[136:][:12] } +func (h *headerV7) Chksum() []byte { return h[148:][:8] } +func (h *headerV7) TypeFlag() []byte { return h[156:][:1] } +func (h *headerV7) LinkName() []byte { return h[157:][:100] } + +type headerGNU [blockSize]byte + +func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerGNU) Magic() []byte { return h[257:][:6] } +func (h *headerGNU) Version() []byte { return h[263:][:2] } +func (h *headerGNU) UserName() []byte { return h[265:][:32] } +func (h *headerGNU) GroupName() []byte { return h[297:][:32] } +func (h *headerGNU) DevMajor() []byte { return h[329:][:8] } +func (h *headerGNU) DevMinor() []byte { return h[337:][:8] } +func (h *headerGNU) AccessTime() []byte { return h[345:][:12] } +func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] } +func (h *headerGNU) Sparse() sparseArray { return (sparseArray)(h[386:][:24*4+1]) } +func (h *headerGNU) RealSize() []byte { return h[483:][:12] } + +type headerSTAR [blockSize]byte + +func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerSTAR) Magic() []byte { return h[257:][:6] } +func (h *headerSTAR) Version() []byte { return h[263:][:2] } +func (h *headerSTAR) UserName() []byte { return h[265:][:32] } +func (h *headerSTAR) GroupName() []byte { return h[297:][:32] } +func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] } +func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] } +func (h *headerSTAR) Prefix() []byte { return h[345:][:131] } +func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] } +func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] } +func (h *headerSTAR) Trailer() []byte { return h[508:][:4] } + +type headerUSTAR [blockSize]byte + +func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerUSTAR) Magic() []byte { return h[257:][:6] } +func (h *headerUSTAR) Version() []byte { return h[263:][:2] } +func (h *headerUSTAR) UserName() []byte { return h[265:][:32] } +func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] } +func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] } +func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] } +func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] } + +type sparseArray []byte + +func (s sparseArray) Entry(i int) sparseNode { return (sparseNode)(s[i*24:]) } +func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } +func (s sparseArray) MaxEntries() int { return len(s) / 24 } + +type sparseNode []byte + +func (s sparseNode) Offset() []byte { return s[00:][:12] } +func (s sparseNode) NumBytes() []byte { return s[12:][:12] } diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index e2a2a5440e04c2..096ef082bf8764 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -29,11 +29,11 @@ const maxNanoSecondIntSize = 9 // The Next method advances to the next file in the archive (including the first), // and then it can be treated as an io.Reader to access the file's data. type Reader struct { - r io.Reader - err error - pad int64 // amount of padding (ignored) after current file entry - curr numBytesReader // reader for current file entry - hdrBuff [blockSize]byte // buffer to use in readHeader + r io.Reader + err error + pad int64 // amount of padding (ignored) after current file entry + curr numBytesReader // reader for current file entry + blk block // buffer to use as temporary local storage } type parser struct { @@ -98,17 +98,6 @@ const ( paxGNUSparseRealSize = "GNU.sparse.realsize" ) -// Keywords for old GNU sparse headers -const ( - oldGNUSparseMainHeaderOffset = 386 - oldGNUSparseMainHeaderIsExtendedOffset = 482 - oldGNUSparseMainHeaderNumEntries = 4 - oldGNUSparseExtendedHeaderIsExtendedOffset = 504 - oldGNUSparseExtendedHeaderNumEntries = 21 - oldGNUSparseOffsetSize = 12 - oldGNUSparseNumBytesSize = 12 -) - // NewReader creates a new Reader reading from r. func NewReader(r io.Reader) *Reader { return &Reader{r: r} } @@ -542,17 +531,6 @@ func (tr *Reader) skipUnread() error { return tr.err } -func (tr *Reader) verifyChecksum(header []byte) bool { - if tr.err != nil { - return false - } - - var p parser - given := p.parseOctal(header[148:156]) - unsigned, signed := checksum(header) - return p.err == nil && (given == unsigned || given == signed) -} - // readHeader reads the next block header and assumes that the underlying reader // is already aligned to a block boundary. // @@ -561,19 +539,16 @@ func (tr *Reader) verifyChecksum(header []byte) bool { // * Exactly 1 block of zeros is read and EOF is hit. // * At least 2 blocks of zeros are read. func (tr *Reader) readHeader() *Header { - header := tr.hdrBuff[:] - copy(header, zeroBlock) - - if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { + if _, tr.err = io.ReadFull(tr.r, tr.blk[:]); tr.err != nil { return nil // io.EOF is okay here } // Two blocks of zero bytes marks the end of the archive. - if bytes.Equal(header, zeroBlock[0:blockSize]) { - if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { + if bytes.Equal(tr.blk[:], zeroBlock[:]) { + if _, tr.err = io.ReadFull(tr.r, tr.blk[:]); tr.err != nil { return nil // io.EOF is okay here } - if bytes.Equal(header, zeroBlock[0:blockSize]) { + if bytes.Equal(tr.blk[:], zeroBlock[:]) { tr.err = io.EOF } else { tr.err = ErrHeader // zero block and then non-zero block @@ -581,71 +556,55 @@ func (tr *Reader) readHeader() *Header { return nil } - if !tr.verifyChecksum(header) { + // Verify the header matches a known format. + format := tr.blk.GetFormat() + if format == formatUnknown { tr.err = ErrHeader return nil } - // Unpack var p parser hdr := new(Header) - s := slicer(header) - - hdr.Name = p.parseString(s.next(100)) - hdr.Mode = p.parseNumeric(s.next(8)) - hdr.Uid = int(p.parseNumeric(s.next(8))) - hdr.Gid = int(p.parseNumeric(s.next(8))) - hdr.Size = p.parseNumeric(s.next(12)) - hdr.ModTime = time.Unix(p.parseNumeric(s.next(12)), 0) - s.next(8) // chksum - hdr.Typeflag = s.next(1)[0] - hdr.Linkname = p.parseString(s.next(100)) - - // The remainder of the header depends on the value of magic. - // The original (v7) version of tar had no explicit magic field, - // so its magic bytes, like the rest of the block, are NULs. - magic := string(s.next(8)) // contains version field as well. - var format string - switch { - case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988) - if string(header[508:512]) == "tar\x00" { - format = "star" - } else { - format = "posix" - } - case magic == "ustar \x00": // old GNU tar - format = "gnu" - } - switch format { - case "posix", "gnu", "star": - hdr.Uname = p.parseString(s.next(32)) - hdr.Gname = p.parseString(s.next(32)) - devmajor := s.next(8) - devminor := s.next(8) + // Unpack the V7 header. + v7 := tr.blk.V7() + hdr.Name = p.parseString(v7.Name()) + hdr.Mode = p.parseNumeric(v7.Mode()) + hdr.Uid = int(p.parseNumeric(v7.UID())) + hdr.Gid = int(p.parseNumeric(v7.GID())) + hdr.Size = p.parseNumeric(v7.Size()) + hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) + hdr.Typeflag = v7.TypeFlag()[0] + hdr.Linkname = p.parseString(v7.LinkName()) + + // Unpack format specific fields. + if format > formatV7 { + ustar := tr.blk.USTAR() + hdr.Uname = p.parseString(ustar.UserName()) + hdr.Gname = p.parseString(ustar.GroupName()) if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock { - hdr.Devmajor = p.parseNumeric(devmajor) - hdr.Devminor = p.parseNumeric(devminor) + hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) + hdr.Devminor = p.parseNumeric(ustar.DevMinor()) } + var prefix string switch format { - case "posix", "gnu": - prefix = p.parseString(s.next(155)) - case "star": - prefix = p.parseString(s.next(131)) - hdr.AccessTime = time.Unix(p.parseNumeric(s.next(12)), 0) - hdr.ChangeTime = time.Unix(p.parseNumeric(s.next(12)), 0) + case formatUSTAR, formatGNU: + // TODO(dsnet): Do not use the prefix field for the GNU format! + // See golang.org/issues/12594 + ustar := tr.blk.USTAR() + prefix = p.parseString(ustar.Prefix()) + case formatSTAR: + star := tr.blk.STAR() + prefix = p.parseString(star.Prefix()) + hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0) + hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0) } if len(prefix) > 0 { hdr.Name = prefix + "/" + hdr.Name } } - if p.err != nil { - tr.err = p.err - return nil - } - nb := hdr.Size if isHeaderOnlyType(hdr.Typeflag) { nb = 0 @@ -662,14 +621,14 @@ func (tr *Reader) readHeader() *Header { // Check for old GNU sparse format entry. if hdr.Typeflag == TypeGNUSparse { // Get the real size of the file. - hdr.Size = p.parseNumeric(header[483:495]) + hdr.Size = p.parseNumeric(tr.blk.GNU().RealSize()) if p.err != nil { tr.err = p.err return nil } // Read the sparse map. - sp := tr.readOldGNUSparseMap(header) + sp := tr.readOldGNUSparseMap(&tr.blk) if tr.err != nil { return nil } @@ -681,26 +640,24 @@ func (tr *Reader) readHeader() *Header { } } + if p.err != nil { + tr.err = p.err + return nil + } + return hdr } // readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format. // The sparse map is stored in the tar header if it's small enough. If it's larger than four entries, // then one or more extension headers are used to store the rest of the sparse map. -func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry { +func (tr *Reader) readOldGNUSparseMap(blk *block) []sparseEntry { var p parser - isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0 - spCap := oldGNUSparseMainHeaderNumEntries - if isExtended { - spCap += oldGNUSparseExtendedHeaderNumEntries - } - sp := make([]sparseEntry, 0, spCap) - s := slicer(header[oldGNUSparseMainHeaderOffset:]) - - // Read the four entries from the main tar header - for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ { - offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize)) - numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize)) + var s sparseArray = blk.GNU().Sparse() + var sp = make([]sparseEntry, 0, s.MaxEntries()) + for i := 0; i < s.MaxEntries(); i++ { + offset := p.parseOctal(s.Entry(i).Offset()) + numBytes := p.parseOctal(s.Entry(i).NumBytes()) if p.err != nil { tr.err = p.err return nil @@ -711,17 +668,17 @@ func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry { sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) } - for isExtended { + for s.IsExtended()[0] > 0 { // There are more entries. Read an extension header and parse its entries. - sparseHeader := make([]byte, blockSize) - if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil { + var blk block + if _, tr.err = io.ReadFull(tr.r, blk[:]); tr.err != nil { return nil } - isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0 - s = slicer(sparseHeader) - for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ { - offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize)) - numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize)) + s = blk.Sparse() + + for i := 0; i < s.MaxEntries(); i++ { + offset := p.parseOctal(s.Entry(i).Offset()) + numBytes := p.parseOctal(s.Entry(i).NumBytes()) if p.err != nil { tr.err = p.err return nil diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go index 944b2d49529adf..426e4434eb7544 100644 --- a/src/archive/tar/writer.go +++ b/src/archive/tar/writer.go @@ -36,10 +36,10 @@ type Writer struct { nb int64 // number of unwritten bytes for current file entry pad int64 // amount of padding to write after current file entry closed bool - usedBinary bool // whether the binary numeric field extension was used - preferPax bool // use pax header instead of binary numeric header - hdrBuff [blockSize]byte // buffer to use in writeHeader when writing a regular header - paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header + usedBinary bool // whether the binary numeric field extension was used + preferPax bool // use PAX header instead of binary numeric header + hdrBuff block // buffer to use in writeHeader when writing a regular header + paxHdrBuff block // buffer to use in writeHeader when writing a PAX header } type formatter struct { @@ -153,27 +153,24 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { // a map to hold pax header records, if any are needed paxHeaders := make(map[string]string) - // TODO(shanemhansen): we might want to use PAX headers for + // TODO(dsnet): we might want to use PAX headers for // subsecond time resolution, but for now let's just capture // too long fields or non ascii characters - var f formatter - var header []byte - // We need to select which scratch buffer to use carefully, // since this method is called recursively to write PAX headers. // If allowPax is true, this is the non-recursive call, and we will use hdrBuff. // If allowPax is false, we are being called by writePAXHeader, and hdrBuff is // already being used by the non-recursive call, so we must use paxHdrBuff. - header = tw.hdrBuff[:] + header := &tw.hdrBuff if !allowPax { - header = tw.paxHdrBuff[:] + header = &tw.paxHdrBuff } - copy(header, zeroBlock) - s := slicer(header) + copy(header[:], zeroBlock[:]) // Wrappers around formatter that automatically sets paxHeaders if the // argument extends beyond the capacity of the input byte slice. + var f formatter var formatString = func(b []byte, s string, paxKeyword string) { needsPaxHeader := paxKeyword != paxNone && len(s) > len(b) || !isASCII(s) if needsPaxHeader { @@ -202,44 +199,33 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { f.formatNumeric(b, x) } - // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax - pathHeaderBytes := s.next(fileNameSize) - - formatString(pathHeaderBytes, hdr.Name, paxPath) - // Handle out of range ModTime carefully. var modTime int64 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) { modTime = hdr.ModTime.Unix() } - f.formatOctal(s.next(8), hdr.Mode) // 100:108 - formatNumeric(s.next(8), int64(hdr.Uid), paxUid) // 108:116 - formatNumeric(s.next(8), int64(hdr.Gid), paxGid) // 116:124 - formatNumeric(s.next(12), hdr.Size, paxSize) // 124:136 - formatNumeric(s.next(12), modTime, paxNone) // 136:148 --- consider using pax for finer granularity - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - - formatString(s.next(100), hdr.Linkname, paxLinkpath) - - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - formatString(s.next(32), hdr.Uname, paxUname) // 265:297 - formatString(s.next(32), hdr.Gname, paxGname) // 297:329 - formatNumeric(s.next(8), hdr.Devmajor, paxNone) // 329:337 - formatNumeric(s.next(8), hdr.Devminor, paxNone) // 337:345 - - // keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax - prefixHeaderBytes := s.next(155) - formatString(prefixHeaderBytes, "", paxNone) // 345:500 prefix + v7 := header.V7() + formatString(v7.Name(), hdr.Name, paxPath) + // TODO(dsnet): The GNU format permits the mode field to be encoded in + // base-256 format. Thus, we can use formatNumeric instead of formatOctal. + f.formatOctal(v7.Mode(), hdr.Mode) + formatNumeric(v7.UID(), int64(hdr.Uid), paxUid) + formatNumeric(v7.GID(), int64(hdr.Gid), paxGid) + formatNumeric(v7.Size(), hdr.Size, paxSize) + // TODO(dsnet): Consider using PAX for finer time granularity. + formatNumeric(v7.ModTime(), modTime, paxNone) + v7.TypeFlag()[0] = hdr.Typeflag + formatString(v7.LinkName(), hdr.Linkname, paxLinkpath) + + ustar := header.USTAR() + formatString(ustar.UserName(), hdr.Uname, paxUname) + formatString(ustar.GroupName(), hdr.Gname, paxGname) + formatNumeric(ustar.DevMajor(), hdr.Devmajor, paxNone) + formatNumeric(ustar.DevMinor(), hdr.Devminor, paxNone) - // Use the GNU magic instead of POSIX magic if we used any GNU extensions. - if tw.usedBinary { - copy(header[257:265], []byte("ustar \x00")) - } - - _, paxPathUsed := paxHeaders[paxPath] // try to use a ustar header when only the name is too long + _, paxPathUsed := paxHeaders[paxPath] if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed { prefix, suffix, ok := splitUSTARPath(hdr.Name) if ok { @@ -247,16 +233,16 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { delete(paxHeaders, paxPath) // Update the path fields - formatString(pathHeaderBytes, suffix, paxNone) - formatString(prefixHeaderBytes, prefix, paxNone) + formatString(v7.Name(), suffix, paxNone) + formatString(ustar.Prefix(), prefix, paxNone) } } - // The chksum field is terminated by a NUL and a space. - // This is different from the other octal fields. - chksum, _ := checksum(header) - f.formatOctal(header[148:155], chksum) // Never fails - header[155] = ' ' + if tw.usedBinary { + header.SetFormat(formatGNU) + } else { + header.SetFormat(formatUSTAR) + } // Check if there were any formatting errors. if f.err != nil { @@ -281,7 +267,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { tw.nb = hdr.Size tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize - _, tw.err = tw.w.Write(header) + _, tw.err = tw.w.Write(header[:]) return tw.err } @@ -289,10 +275,10 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { // If the path is not splittable, then it will return ("", "", false). func splitUSTARPath(name string) (prefix, suffix string, ok bool) { length := len(name) - if length <= fileNameSize || !isASCII(name) { + if length <= nameSize || !isASCII(name) { return "", "", false - } else if length > fileNamePrefixSize+1 { - length = fileNamePrefixSize + 1 + } else if length > prefixSize+1 { + length = prefixSize + 1 } else if name[length-1] == '/' { length-- } @@ -300,7 +286,7 @@ func splitUSTARPath(name string) (prefix, suffix string, ok bool) { i := strings.LastIndex(name[:length], "/") nlen := len(name) - i - 1 // nlen is length of suffix plen := i // plen is length of prefix - if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize { + if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize { return "", "", false } return name[:i], name[i+1:], true @@ -323,8 +309,8 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro fullName := path.Join(dir, "PaxHeaders.0", file) ascii := toASCII(fullName) - if len(ascii) > 100 { - ascii = ascii[:100] + if len(ascii) > nameSize { + ascii = ascii[:nameSize] } ext.Name = ascii // Construct the body @@ -407,7 +393,7 @@ func (tw *Writer) Close() error { // trailer: two zero blocks for i := 0; i < 2; i++ { - _, tw.err = tw.w.Write(zeroBlock) + _, tw.err = tw.w.Write(zeroBlock[:]) if tw.err != nil { break } diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go index 6e91d907ce96bd..27aa8e5dab6724 100644 --- a/src/archive/tar/writer_test.go +++ b/src/archive/tar/writer_test.go @@ -587,17 +587,17 @@ func TestSplitUSTARPath(t *testing.T) { {"", "", "", false}, {"abc", "", "", false}, {"用戶名", "", "", false}, - {sr("a", fileNameSize), "", "", false}, - {sr("a", fileNameSize) + "/", "", "", false}, - {sr("a", fileNameSize) + "/a", sr("a", fileNameSize), "a", true}, - {sr("a", fileNamePrefixSize) + "/", "", "", false}, - {sr("a", fileNamePrefixSize) + "/a", sr("a", fileNamePrefixSize), "a", true}, - {sr("a", fileNameSize+1), "", "", false}, - {sr("/", fileNameSize+1), sr("/", fileNameSize-1), "/", true}, - {sr("a", fileNamePrefixSize) + "/" + sr("b", fileNameSize), - sr("a", fileNamePrefixSize), sr("b", fileNameSize), true}, - {sr("a", fileNamePrefixSize) + "//" + sr("b", fileNameSize), "", "", false}, - {sr("a/", fileNameSize), sr("a/", 77) + "a", sr("a/", 22), true}, + {sr("a", nameSize), "", "", false}, + {sr("a", nameSize) + "/", "", "", false}, + {sr("a", nameSize) + "/a", sr("a", nameSize), "a", true}, + {sr("a", prefixSize) + "/", "", "", false}, + {sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true}, + {sr("a", nameSize+1), "", "", false}, + {sr("/", nameSize+1), sr("/", nameSize-1), "/", true}, + {sr("a", prefixSize) + "/" + sr("b", nameSize), + sr("a", prefixSize), sr("b", nameSize), true}, + {sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false}, + {sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true}, } for _, v := range vectors { diff --git a/src/archive/zip/struct.go b/src/archive/zip/struct.go index 5ee4f88f8036ba..e92d02f8a2e872 100644 --- a/src/archive/zip/struct.go +++ b/src/archive/zip/struct.go @@ -5,7 +5,7 @@ /* Package zip provides support for reading and writing ZIP archives. -See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT +See: https://www.pkware.com/documents/casestudies/APPNOTE.TXT This package does not support disk spanning. diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index d769a6aaa98d0e..858048696e4ed1 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -1475,7 +1475,7 @@ func BenchmarkReaderWriteToOptimal(b *testing.B) { b.Fatal("ioutil.Discard doesn't support ReaderFrom") } for i := 0; i < b.N; i++ { - r.Seek(0, 0) + r.Seek(0, io.SeekStart) srcReader.Reset(onlyReader{r}) n, err := srcReader.WriteTo(ioutil.Discard) if err != nil { diff --git a/src/bytes/reader.go b/src/bytes/reader.go index 83826c80c417af..28cfc7a97884a7 100644 --- a/src/bytes/reader.go +++ b/src/bytes/reader.go @@ -108,11 +108,11 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) { r.prevRune = -1 var abs int64 switch whence { - case 0: + case io.SeekStart: abs = offset - case 1: + case io.SeekCurrent: abs = r.i + offset - case 2: + case io.SeekEnd: abs = int64(len(r.s)) + offset default: return 0, errors.New("bytes.Reader.Seek: invalid whence") diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go index b5c78506189e50..7b3034d4e0d90c 100644 --- a/src/bytes/reader_test.go +++ b/src/bytes/reader_test.go @@ -188,7 +188,7 @@ var UnreadRuneErrorTests = []struct { {"Read", func(r *Reader) { r.Read([]byte{0}) }}, {"ReadByte", func(r *Reader) { r.ReadByte() }}, {"UnreadRune", func(r *Reader) { r.UnreadRune() }}, - {"Seek", func(r *Reader) { r.Seek(0, 1) }}, + {"Seek", func(r *Reader) { r.Seek(0, io.SeekCurrent) }}, {"WriteTo", func(r *Reader) { r.WriteTo(&Buffer{}) }}, } diff --git a/src/cmd/asm/internal/arch/amd64.go b/src/cmd/asm/internal/arch/amd64.go new file mode 100644 index 00000000000000..625e136d1db6a2 --- /dev/null +++ b/src/cmd/asm/internal/arch/amd64.go @@ -0,0 +1,28 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file encapsulates some of the odd characteristics of the +// AMD64 instruction set, to minimize its interaction +// with the core of the assembler. + +package arch + +import ( + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +// IsAMD4OP reports whether the op (as defined by an ppc64.A* constant) is +// The FMADD-like instructions behave similarly. +func IsAMD4OP(op obj.As) bool { + switch op { + case x86.AVPERM2F128, + x86.AVPALIGNR, + x86.AVPERM2I128, + x86.AVINSERTI128, + x86.AVPBLENDD: + return true + } + return false +} diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index 24906e2cce200c..c9c64203ae68bd 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -568,6 +568,15 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] + case sys.AMD64: + // Catch missing operand here, because we store immediate as part of From3, and can't distinguish + // missing operand from legal value 0 in obj/x86/asm6. + if arch.IsAMD4OP(op) { + p.errorf("4 operands required, but only 3 are provided for %s instruction", obj.Aconv(op)) + } + prog.From = a[0] + prog.From3 = newAddr(a[1]) + prog.To = a[2] case sys.ARM64: // ARM64 instructions with one input and two outputs. if arch.IsARM64STLXR(op) { @@ -583,7 +592,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] - case sys.AMD64, sys.I386: + case sys.I386: prog.From = a[0] prog.From3 = newAddr(a[1]) prog.To = a[2] @@ -640,6 +649,23 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.Reg = r1 break } + if p.arch.Family == sys.AMD64 { + // 4 operand instruction have form ymm1, ymm2, ymm3/m256, imm8 + // So From3 is always just a register, so we store imm8 in Offset field, + // to avoid increasing size of Prog. + prog.From = a[1] + prog.From3 = newAddr(a[2]) + if a[0].Type != obj.TYPE_CONST { + p.errorf("first operand must be an immediate in %s instruction", obj.Aconv(op)) + } + if prog.From3.Type != obj.TYPE_REG { + p.errorf("third operand must be a register in %s instruction", obj.Aconv(op)) + } + prog.From3.Offset = int64(p.getImmediate(prog, op, &a[0])) + prog.To = a[3] + prog.RegTo2 = -1 + break + } if p.arch.Family == sys.ARM64 { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) diff --git a/src/cmd/asm/internal/asm/testdata/amd64enc.s b/src/cmd/asm/internal/asm/testdata/amd64enc.s index 63fdcac27db840..22dfe127b3db0b 100644 --- a/src/cmd/asm/internal/asm/testdata/amd64enc.s +++ b/src/cmd/asm/internal/asm/testdata/amd64enc.s @@ -5008,22 +5008,22 @@ TEXT asmtest(SB),7,$0 RORB $7, (R11) // 41c00b07 RORB $7, DL // c0ca07 RORB $7, R11 // 41c0cb07 - //TODO: RORXL $7, (BX), DX // c4e37bf01307 - //TODO: RORXL $7, (R11), DX // c4c37bf01307 - //TODO: RORXL $7, DX, DX // c4e37bf0d207 - //TODO: RORXL $7, R11, DX // c4c37bf0d307 - //TODO: RORXL $7, (BX), R11 // c4637bf01b07 - //TODO: RORXL $7, (R11), R11 // c4437bf01b07 - //TODO: RORXL $7, DX, R11 // c4637bf0da07 - //TODO: RORXL $7, R11, R11 // c4437bf0db07 - //TODO: RORXQ $7, (BX), DX // c4e3fbf01307 - //TODO: RORXQ $7, (R11), DX // c4c3fbf01307 - //TODO: RORXQ $7, DX, DX // c4e3fbf0d207 - //TODO: RORXQ $7, R11, DX // c4c3fbf0d307 - //TODO: RORXQ $7, (BX), R11 // c463fbf01b07 - //TODO: RORXQ $7, (R11), R11 // c443fbf01b07 - //TODO: RORXQ $7, DX, R11 // c463fbf0da07 - //TODO: RORXQ $7, R11, R11 // c443fbf0db07 + RORXL $7, (BX), DX // c4e37bf01307 + RORXL $7, (R11), DX // c4c37bf01307 + RORXL $7, DX, DX // c4e37bf0d207 + RORXL $7, R11, DX // c4c37bf0d307 + RORXL $7, (BX), R11 // c4637bf01b07 + RORXL $7, (R11), R11 // c4437bf01b07 + RORXL $7, DX, R11 // c4637bf0da07 + RORXL $7, R11, R11 // c4437bf0db07 + RORXQ $7, (BX), DX // c4e3fbf01307 + RORXQ $7, (R11), DX // c4c3fbf01307 + RORXQ $7, DX, DX // c4e3fbf0d207 + RORXQ $7, R11, DX // c4c3fbf0d307 + RORXQ $7, (BX), R11 // c463fbf01b07 + RORXQ $7, (R11), R11 // c443fbf01b07 + RORXQ $7, DX, R11 // c463fbf0da07 + RORXQ $7, R11, R11 // c443fbf0db07 ROUNDPD $7, (BX), X2 // 660f3a091307 ROUNDPD $7, (R11), X2 // 66410f3a091307 ROUNDPD $7, X2, X2 // 660f3a09d207 @@ -7420,14 +7420,14 @@ TEXT asmtest(SB),7,$0 //TODO: VINSERTF128 $7, (R11), Y15, Y11 // c44305181b07 //TODO: VINSERTF128 $7, X2, Y15, Y11 // c4630518da07 //TODO: VINSERTF128 $7, X11, Y15, Y11 // c4430518db07 - //TODO: VINSERTI128 $7, (BX), Y15, Y2 // c4e305381307 - //TODO: VINSERTI128 $7, (R11), Y15, Y2 // c4c305381307 - //TODO: VINSERTI128 $7, X2, Y15, Y2 // c4e30538d207 - //TODO: VINSERTI128 $7, X11, Y15, Y2 // c4c30538d307 - //TODO: VINSERTI128 $7, (BX), Y15, Y11 // c46305381b07 - //TODO: VINSERTI128 $7, (R11), Y15, Y11 // c44305381b07 - //TODO: VINSERTI128 $7, X2, Y15, Y11 // c4630538da07 - //TODO: VINSERTI128 $7, X11, Y15, Y11 // c4430538db07 + VINSERTI128 $7, (BX), Y15, Y2 // c4e305381307 + VINSERTI128 $7, (R11), Y15, Y2 // c4c305381307 + VINSERTI128 $7, X2, Y15, Y2 // c4e30538d207 + VINSERTI128 $7, X11, Y15, Y2 // c4c30538d307 + VINSERTI128 $7, (BX), Y15, Y11 // c46305381b07 + VINSERTI128 $7, (R11), Y15, Y11 // c44305381b07 + VINSERTI128 $7, X2, Y15, Y11 // c4630538da07 + VINSERTI128 $7, X11, Y15, Y11 // c4430538db07 //TODO: VINSERTPS $7, (BX), X9, X2 // c4e331211307 //TODO: VINSERTPS $7, (R11), X9, X2 // c4c331211307 //TODO: VINSERTPS $7, X2, X9, X2 // c4e33121d207 @@ -8142,38 +8142,38 @@ TEXT asmtest(SB),7,$0 //TODO: VPADDB (R11), Y15, Y11 // c44105fc1b //TODO: VPADDB Y2, Y15, Y11 // c46105fcda or c505fcda //TODO: VPADDB Y11, Y15, Y11 // c44105fcdb - //TODO: VPADDD (BX), X9, X2 // c4e131fe13 or c5b1fe13 - //TODO: VPADDD (R11), X9, X2 // c4c131fe13 - //TODO: VPADDD X2, X9, X2 // c4e131fed2 or c5b1fed2 - //TODO: VPADDD X11, X9, X2 // c4c131fed3 - //TODO: VPADDD (BX), X9, X11 // c46131fe1b or c531fe1b - //TODO: VPADDD (R11), X9, X11 // c44131fe1b - //TODO: VPADDD X2, X9, X11 // c46131feda or c531feda - //TODO: VPADDD X11, X9, X11 // c44131fedb - //TODO: VPADDD (BX), Y15, Y2 // c4e105fe13 or c585fe13 - //TODO: VPADDD (R11), Y15, Y2 // c4c105fe13 - //TODO: VPADDD Y2, Y15, Y2 // c4e105fed2 or c585fed2 - //TODO: VPADDD Y11, Y15, Y2 // c4c105fed3 - //TODO: VPADDD (BX), Y15, Y11 // c46105fe1b or c505fe1b - //TODO: VPADDD (R11), Y15, Y11 // c44105fe1b - //TODO: VPADDD Y2, Y15, Y11 // c46105feda or c505feda - //TODO: VPADDD Y11, Y15, Y11 // c44105fedb - //TODO: VPADDQ (BX), X9, X2 // c4e131d413 or c5b1d413 - //TODO: VPADDQ (R11), X9, X2 // c4c131d413 - //TODO: VPADDQ X2, X9, X2 // c4e131d4d2 or c5b1d4d2 - //TODO: VPADDQ X11, X9, X2 // c4c131d4d3 - //TODO: VPADDQ (BX), X9, X11 // c46131d41b or c531d41b - //TODO: VPADDQ (R11), X9, X11 // c44131d41b - //TODO: VPADDQ X2, X9, X11 // c46131d4da or c531d4da - //TODO: VPADDQ X11, X9, X11 // c44131d4db - //TODO: VPADDQ (BX), Y15, Y2 // c4e105d413 or c585d413 - //TODO: VPADDQ (R11), Y15, Y2 // c4c105d413 - //TODO: VPADDQ Y2, Y15, Y2 // c4e105d4d2 or c585d4d2 - //TODO: VPADDQ Y11, Y15, Y2 // c4c105d4d3 - //TODO: VPADDQ (BX), Y15, Y11 // c46105d41b or c505d41b - //TODO: VPADDQ (R11), Y15, Y11 // c44105d41b - //TODO: VPADDQ Y2, Y15, Y11 // c46105d4da or c505d4da - //TODO: VPADDQ Y11, Y15, Y11 // c44105d4db + VPADDD (BX), X9, X2 // c4e131fe13 or c5b1fe13 + VPADDD (R11), X9, X2 // c4c131fe13 + VPADDD X2, X9, X2 // c4e131fed2 or c5b1fed2 + VPADDD X11, X9, X2 // c4c131fed3 + VPADDD (BX), X9, X11 // c46131fe1b or c531fe1b + VPADDD (R11), X9, X11 // c44131fe1b + VPADDD X2, X9, X11 // c46131feda or c531feda + VPADDD X11, X9, X11 // c44131fedb + VPADDD (BX), Y15, Y2 // c4e105fe13 or c585fe13 + VPADDD (R11), Y15, Y2 // c4c105fe13 + VPADDD Y2, Y15, Y2 // c4e105fed2 or c585fed2 + VPADDD Y11, Y15, Y2 // c4c105fed3 + VPADDD (BX), Y15, Y11 // c46105fe1b or c505fe1b + VPADDD (R11), Y15, Y11 // c44105fe1b + VPADDD Y2, Y15, Y11 // c46105feda or c505feda + VPADDD Y11, Y15, Y11 // c44105fedb + VPADDQ (BX), X9, X2 // c4e131d413 or c5b1d413 + VPADDQ (R11), X9, X2 // c4c131d413 + VPADDQ X2, X9, X2 // c4e131d4d2 or c5b1d4d2 + VPADDQ X11, X9, X2 // c4c131d4d3 + VPADDQ (BX), X9, X11 // c46131d41b or c531d41b + VPADDQ (R11), X9, X11 // c44131d41b + VPADDQ X2, X9, X11 // c46131d4da or c531d4da + VPADDQ X11, X9, X11 // c44131d4db + VPADDQ (BX), Y15, Y2 // c4e105d413 or c585d413 + VPADDQ (R11), Y15, Y2 // c4c105d413 + VPADDQ Y2, Y15, Y2 // c4e105d4d2 or c585d4d2 + VPADDQ Y11, Y15, Y2 // c4c105d4d3 + VPADDQ (BX), Y15, Y11 // c46105d41b or c505d41b + VPADDQ (R11), Y15, Y11 // c44105d41b + VPADDQ Y2, Y15, Y11 // c46105d4da or c505d4da + VPADDQ Y11, Y15, Y11 // c44105d4db //TODO: VPADDSB (BX), X9, X2 // c4e131ec13 or c5b1ec13 //TODO: VPADDSB (R11), X9, X2 // c4c131ec13 //TODO: VPADDSB X2, X9, X2 // c4e131ecd2 or c5b1ecd2 @@ -8262,14 +8262,14 @@ TEXT asmtest(SB),7,$0 //TODO: VPALIGNR $7, (R11), X9, X11 // c443310f1b07 //TODO: VPALIGNR $7, X2, X9, X11 // c463310fda07 //TODO: VPALIGNR $7, X11, X9, X11 // c443310fdb07 - //TODO: VPALIGNR $7, (BX), Y15, Y2 // c4e3050f1307 - //TODO: VPALIGNR $7, (R11), Y15, Y2 // c4c3050f1307 - //TODO: VPALIGNR $7, Y2, Y15, Y2 // c4e3050fd207 - //TODO: VPALIGNR $7, Y11, Y15, Y2 // c4c3050fd307 - //TODO: VPALIGNR $7, (BX), Y15, Y11 // c463050f1b07 - //TODO: VPALIGNR $7, (R11), Y15, Y11 // c443050f1b07 - //TODO: VPALIGNR $7, Y2, Y15, Y11 // c463050fda07 - //TODO: VPALIGNR $7, Y11, Y15, Y11 // c443050fdb07 + VPALIGNR $7, (BX), Y15, Y2 // c4e3050f1307 + VPALIGNR $7, (R11), Y15, Y2 // c4c3050f1307 + VPALIGNR $7, Y2, Y15, Y2 // c4e3050fd207 + VPALIGNR $7, Y11, Y15, Y2 // c4c3050fd307 + VPALIGNR $7, (BX), Y15, Y11 // c463050f1b07 + VPALIGNR $7, (R11), Y15, Y11 // c443050f1b07 + VPALIGNR $7, Y2, Y15, Y11 // c463050fda07 + VPALIGNR $7, Y11, Y15, Y11 // c443050fdb07 VPAND (BX), X9, X2 // c4e131db13 or c5b1db13 VPAND (R11), X9, X2 // c4c131db13 VPAND X2, X9, X2 // c4e131dbd2 or c5b1dbd2 @@ -8342,14 +8342,14 @@ TEXT asmtest(SB),7,$0 //TODO: VPBLENDD $7, (R11), X9, X11 // c44331021b07 //TODO: VPBLENDD $7, X2, X9, X11 // c4633102da07 //TODO: VPBLENDD $7, X11, X9, X11 // c4433102db07 - //TODO: VPBLENDD $7, (BX), Y15, Y2 // c4e305021307 - //TODO: VPBLENDD $7, (R11), Y15, Y2 // c4c305021307 - //TODO: VPBLENDD $7, Y2, Y15, Y2 // c4e30502d207 - //TODO: VPBLENDD $7, Y11, Y15, Y2 // c4c30502d307 - //TODO: VPBLENDD $7, (BX), Y15, Y11 // c46305021b07 - //TODO: VPBLENDD $7, (R11), Y15, Y11 // c44305021b07 - //TODO: VPBLENDD $7, Y2, Y15, Y11 // c4630502da07 - //TODO: VPBLENDD $7, Y11, Y15, Y11 // c4430502db07 + VPBLENDD $7, (BX), Y15, Y2 // c4e305021307 + VPBLENDD $7, (R11), Y15, Y2 // c4c305021307 + VPBLENDD $7, Y2, Y15, Y2 // c4e30502d207 + VPBLENDD $7, Y11, Y15, Y2 // c4c30502d307 + VPBLENDD $7, (BX), Y15, Y11 // c46305021b07 + VPBLENDD $7, (R11), Y15, Y11 // c44305021b07 + VPBLENDD $7, Y2, Y15, Y11 // c4630502da07 + VPBLENDD $7, Y11, Y15, Y11 // c4430502db07 //TODO: VPBLENDVB XMM12, (BX), X9, X2 // c4e3314c13c0 //TODO: VPBLENDVB XMM12, (R11), X9, X2 // c4c3314c13c0 //TODO: VPBLENDVB XMM12, X2, X9, X2 // c4e3314cd2c0 @@ -8614,22 +8614,22 @@ TEXT asmtest(SB),7,$0 //TODO: VPCMPISTRM $7, (R11), X11 // c44379621b07 //TODO: VPCMPISTRM $7, X2, X11 // c4637962da07 //TODO: VPCMPISTRM $7, X11, X11 // c4437962db07 - //TODO: VPERM2F128 $7, (BX), Y15, Y2 // c4e305061307 - //TODO: VPERM2F128 $7, (R11), Y15, Y2 // c4c305061307 - //TODO: VPERM2F128 $7, Y2, Y15, Y2 // c4e30506d207 - //TODO: VPERM2F128 $7, Y11, Y15, Y2 // c4c30506d307 - //TODO: VPERM2F128 $7, (BX), Y15, Y11 // c46305061b07 - //TODO: VPERM2F128 $7, (R11), Y15, Y11 // c44305061b07 - //TODO: VPERM2F128 $7, Y2, Y15, Y11 // c4630506da07 - //TODO: VPERM2F128 $7, Y11, Y15, Y11 // c4430506db07 - //TODO: VPERM2I128 $7, (BX), Y15, Y2 // c4e305461307 - //TODO: VPERM2I128 $7, (R11), Y15, Y2 // c4c305461307 - //TODO: VPERM2I128 $7, Y2, Y15, Y2 // c4e30546d207 - //TODO: VPERM2I128 $7, Y11, Y15, Y2 // c4c30546d307 - //TODO: VPERM2I128 $7, (BX), Y15, Y11 // c46305461b07 - //TODO: VPERM2I128 $7, (R11), Y15, Y11 // c44305461b07 - //TODO: VPERM2I128 $7, Y2, Y15, Y11 // c4630546da07 - //TODO: VPERM2I128 $7, Y11, Y15, Y11 // c4430546db07 + VPERM2F128 $7, (BX), Y15, Y2 // c4e305061307 + VPERM2F128 $7, (R11), Y15, Y2 // c4c305061307 + VPERM2F128 $7, Y2, Y15, Y2 // c4e30506d207 + VPERM2F128 $7, Y11, Y15, Y2 // c4c30506d307 + VPERM2F128 $7, (BX), Y15, Y11 // c46305061b07 + VPERM2F128 $7, (R11), Y15, Y11 // c44305061b07 + VPERM2F128 $7, Y2, Y15, Y11 // c4630506da07 + VPERM2F128 $7, Y11, Y15, Y11 // c4430506db07 + VPERM2I128 $7, (BX), Y15, Y2 // c4e305461307 + VPERM2I128 $7, (R11), Y15, Y2 // c4c305461307 + VPERM2I128 $7, Y2, Y15, Y2 // c4e30546d207 + VPERM2I128 $7, Y11, Y15, Y2 // c4c30546d307 + VPERM2I128 $7, (BX), Y15, Y11 // c46305461b07 + VPERM2I128 $7, (R11), Y15, Y11 // c44305461b07 + VPERM2I128 $7, Y2, Y15, Y11 // c4630546da07 + VPERM2I128 $7, Y11, Y15, Y11 // c4430546db07 //TODO: VPERMD (BX), Y15, Y2 // c4e2053613 //TODO: VPERMD (R11), Y15, Y2 // c4c2053613 //TODO: VPERMD Y2, Y15, Y2 // c4e20536d2 @@ -9462,22 +9462,22 @@ TEXT asmtest(SB),7,$0 //TODO: VPMULUDQ (R11), Y15, Y11 // c44105f41b //TODO: VPMULUDQ Y2, Y15, Y11 // c46105f4da or c505f4da //TODO: VPMULUDQ Y11, Y15, Y11 // c44105f4db - //TODO: VPOR (BX), X9, X2 // c4e131eb13 or c5b1eb13 - //TODO: VPOR (R11), X9, X2 // c4c131eb13 - //TODO: VPOR X2, X9, X2 // c4e131ebd2 or c5b1ebd2 - //TODO: VPOR X11, X9, X2 // c4c131ebd3 - //TODO: VPOR (BX), X9, X11 // c46131eb1b or c531eb1b - //TODO: VPOR (R11), X9, X11 // c44131eb1b - //TODO: VPOR X2, X9, X11 // c46131ebda or c531ebda - //TODO: VPOR X11, X9, X11 // c44131ebdb - //TODO: VPOR (BX), Y15, Y2 // c4e105eb13 or c585eb13 - //TODO: VPOR (R11), Y15, Y2 // c4c105eb13 - //TODO: VPOR Y2, Y15, Y2 // c4e105ebd2 or c585ebd2 - //TODO: VPOR Y11, Y15, Y2 // c4c105ebd3 - //TODO: VPOR (BX), Y15, Y11 // c46105eb1b or c505eb1b - //TODO: VPOR (R11), Y15, Y11 // c44105eb1b - //TODO: VPOR Y2, Y15, Y11 // c46105ebda or c505ebda - //TODO: VPOR Y11, Y15, Y11 // c44105ebdb + VPOR (BX), X9, X2 // c4e131eb13 or c5b1eb13 + VPOR (R11), X9, X2 // c4c131eb13 + VPOR X2, X9, X2 // c4e131ebd2 or c5b1ebd2 + VPOR X11, X9, X2 // c4c131ebd3 + VPOR (BX), X9, X11 // c46131eb1b or c531eb1b + VPOR (R11), X9, X11 // c44131eb1b + VPOR X2, X9, X11 // c46131ebda or c531ebda + VPOR X11, X9, X11 // c44131ebdb + VPOR (BX), Y15, Y2 // c4e105eb13 or c585eb13 + VPOR (R11), Y15, Y2 // c4c105eb13 + VPOR Y2, Y15, Y2 // c4e105ebd2 or c585ebd2 + VPOR Y11, Y15, Y2 // c4c105ebd3 + VPOR (BX), Y15, Y11 // c46105eb1b or c505eb1b + VPOR (R11), Y15, Y11 // c44105eb1b + VPOR Y2, Y15, Y11 // c46105ebda or c505ebda + VPOR Y11, Y15, Y11 // c44105ebdb //TODO: VPSADBW (BX), X9, X2 // c4e131f613 or c5b1f613 //TODO: VPSADBW (R11), X9, X2 // c4c131f613 //TODO: VPSADBW X2, X9, X2 // c4e131f6d2 or c5b1f6d2 @@ -9494,38 +9494,38 @@ TEXT asmtest(SB),7,$0 //TODO: VPSADBW (R11), Y15, Y11 // c44105f61b //TODO: VPSADBW Y2, Y15, Y11 // c46105f6da or c505f6da //TODO: VPSADBW Y11, Y15, Y11 // c44105f6db - //TODO: VPSHUFB (BX), X9, X2 // c4e2310013 - //TODO: VPSHUFB (R11), X9, X2 // c4c2310013 - //TODO: VPSHUFB X2, X9, X2 // c4e23100d2 - //TODO: VPSHUFB X11, X9, X2 // c4c23100d3 - //TODO: VPSHUFB (BX), X9, X11 // c46231001b - //TODO: VPSHUFB (R11), X9, X11 // c44231001b - //TODO: VPSHUFB X2, X9, X11 // c4623100da - //TODO: VPSHUFB X11, X9, X11 // c4423100db - //TODO: VPSHUFB (BX), Y15, Y2 // c4e2050013 - //TODO: VPSHUFB (R11), Y15, Y2 // c4c2050013 - //TODO: VPSHUFB Y2, Y15, Y2 // c4e20500d2 - //TODO: VPSHUFB Y11, Y15, Y2 // c4c20500d3 - //TODO: VPSHUFB (BX), Y15, Y11 // c46205001b - //TODO: VPSHUFB (R11), Y15, Y11 // c44205001b - //TODO: VPSHUFB Y2, Y15, Y11 // c4620500da - //TODO: VPSHUFB Y11, Y15, Y11 // c4420500db - //TODO: VPSHUFD $7, (BX), X2 // c4e179701307 or c5f9701307 - //TODO: VPSHUFD $7, (R11), X2 // c4c179701307 - //TODO: VPSHUFD $7, X2, X2 // c4e17970d207 or c5f970d207 - //TODO: VPSHUFD $7, X11, X2 // c4c17970d307 - //TODO: VPSHUFD $7, (BX), X11 // c46179701b07 or c579701b07 - //TODO: VPSHUFD $7, (R11), X11 // c44179701b07 - //TODO: VPSHUFD $7, X2, X11 // c4617970da07 or c57970da07 - //TODO: VPSHUFD $7, X11, X11 // c4417970db07 - //TODO: VPSHUFD $7, (BX), Y2 // c4e17d701307 or c5fd701307 - //TODO: VPSHUFD $7, (R11), Y2 // c4c17d701307 - //TODO: VPSHUFD $7, Y2, Y2 // c4e17d70d207 or c5fd70d207 - //TODO: VPSHUFD $7, Y11, Y2 // c4c17d70d307 - //TODO: VPSHUFD $7, (BX), Y11 // c4617d701b07 or c57d701b07 - //TODO: VPSHUFD $7, (R11), Y11 // c4417d701b07 - //TODO: VPSHUFD $7, Y2, Y11 // c4617d70da07 or c57d70da07 - //TODO: VPSHUFD $7, Y11, Y11 // c4417d70db07 + VPSHUFB (BX), X9, X2 // c4e2310013 + VPSHUFB (R11), X9, X2 // c4c2310013 + VPSHUFB X2, X9, X2 // c4e23100d2 + VPSHUFB X11, X9, X2 // c4c23100d3 + VPSHUFB (BX), X9, X11 // c46231001b + VPSHUFB (R11), X9, X11 // c44231001b + VPSHUFB X2, X9, X11 // c4623100da + VPSHUFB X11, X9, X11 // c4423100db + VPSHUFB (BX), Y15, Y2 // c4e2050013 + VPSHUFB (R11), Y15, Y2 // c4c2050013 + VPSHUFB Y2, Y15, Y2 // c4e20500d2 + VPSHUFB Y11, Y15, Y2 // c4c20500d3 + VPSHUFB (BX), Y15, Y11 // c46205001b + VPSHUFB (R11), Y15, Y11 // c44205001b + VPSHUFB Y2, Y15, Y11 // c4620500da + VPSHUFB Y11, Y15, Y11 // c4420500db + VPSHUFD $7, (BX), X2 // c4e179701307 or c5f9701307 + VPSHUFD $7, (R11), X2 // c4c179701307 + VPSHUFD $7, X2, X2 // c4e17970d207 or c5f970d207 + VPSHUFD $7, X11, X2 // c4c17970d307 + VPSHUFD $7, (BX), X11 // c46179701b07 or c579701b07 + VPSHUFD $7, (R11), X11 // c44179701b07 + VPSHUFD $7, X2, X11 // c4617970da07 or c57970da07 + VPSHUFD $7, X11, X11 // c4417970db07 + VPSHUFD $7, (BX), Y2 // c4e17d701307 or c5fd701307 + VPSHUFD $7, (R11), Y2 // c4c17d701307 + VPSHUFD $7, Y2, Y2 // c4e17d70d207 or c5fd70d207 + VPSHUFD $7, Y11, Y2 // c4c17d70d307 + VPSHUFD $7, (BX), Y11 // c4617d701b07 or c57d701b07 + VPSHUFD $7, (R11), Y11 // c4417d701b07 + VPSHUFD $7, Y2, Y11 // c4617d70da07 or c57d70da07 + VPSHUFD $7, Y11, Y11 // c4417d70db07 //TODO: VPSHUFHW $7, (BX), X2 // c4e17a701307 or c5fa701307 //TODO: VPSHUFHW $7, (R11), X2 // c4c17a701307 //TODO: VPSHUFHW $7, X2, X2 // c4e17a70d207 or c5fa70d207 @@ -9606,30 +9606,30 @@ TEXT asmtest(SB),7,$0 //TODO: VPSIGNW (R11), Y15, Y11 // c44205091b //TODO: VPSIGNW Y2, Y15, Y11 // c4620509da //TODO: VPSIGNW Y11, Y15, Y11 // c4420509db - //TODO: VPSLLD (BX), X9, X2 // c4e131f213 or c5b1f213 - //TODO: VPSLLD (R11), X9, X2 // c4c131f213 - //TODO: VPSLLD X2, X9, X2 // c4e131f2d2 or c5b1f2d2 - //TODO: VPSLLD X11, X9, X2 // c4c131f2d3 - //TODO: VPSLLD (BX), X9, X11 // c46131f21b or c531f21b - //TODO: VPSLLD (R11), X9, X11 // c44131f21b - //TODO: VPSLLD X2, X9, X11 // c46131f2da or c531f2da - //TODO: VPSLLD X11, X9, X11 // c44131f2db - //TODO: VPSLLD $7, X2, X9 // c4e13172f207 or c5b172f207 - //TODO: VPSLLD $7, X11, X9 // c4c13172f307 - //TODO: VPSLLDQ $7, X2, X9 // c4e13173fa07 or c5b173fa07 - //TODO: VPSLLDQ $7, X11, X9 // c4c13173fb07 - //TODO: VPSLLDQ $7, Y2, Y15 // c4e10573fa07 or c58573fa07 - //TODO: VPSLLDQ $7, Y11, Y15 // c4c10573fb07 - //TODO: VPSLLQ (BX), X9, X2 // c4e131f313 or c5b1f313 - //TODO: VPSLLQ (R11), X9, X2 // c4c131f313 - //TODO: VPSLLQ X2, X9, X2 // c4e131f3d2 or c5b1f3d2 - //TODO: VPSLLQ X11, X9, X2 // c4c131f3d3 - //TODO: VPSLLQ (BX), X9, X11 // c46131f31b or c531f31b - //TODO: VPSLLQ (R11), X9, X11 // c44131f31b - //TODO: VPSLLQ X2, X9, X11 // c46131f3da or c531f3da - //TODO: VPSLLQ X11, X9, X11 // c44131f3db - //TODO: VPSLLQ $7, X2, X9 // c4e13173f207 or c5b173f207 - //TODO: VPSLLQ $7, X11, X9 // c4c13173f307 + VPSLLD (BX), X9, X2 // c4e131f213 or c5b1f213 + VPSLLD (R11), X9, X2 // c4c131f213 + VPSLLD X2, X9, X2 // c4e131f2d2 or c5b1f2d2 + VPSLLD X11, X9, X2 // c4c131f2d3 + VPSLLD (BX), X9, X11 // c46131f21b or c531f21b + VPSLLD (R11), X9, X11 // c44131f21b + VPSLLD X2, X9, X11 // c46131f2da or c531f2da + VPSLLD X11, X9, X11 // c44131f2db + VPSLLD $7, X2, X9 // c4e13172f207 or c5b172f207 + VPSLLD $7, X11, X9 // c4c13172f307 + VPSLLDQ $7, X2, X9 // c4e13173fa07 or c5b173fa07 + VPSLLDQ $7, X11, X9 // c4c13173fb07 + VPSLLDQ $7, Y2, Y15 // c4e10573fa07 or c58573fa07 + VPSLLDQ $7, Y11, Y15 // c4c10573fb07 + VPSLLQ (BX), X9, X2 // c4e131f313 or c5b1f313 + VPSLLQ (R11), X9, X2 // c4c131f313 + VPSLLQ X2, X9, X2 // c4e131f3d2 or c5b1f3d2 + VPSLLQ X11, X9, X2 // c4c131f3d3 + VPSLLQ (BX), X9, X11 // c46131f31b or c531f31b + VPSLLQ (R11), X9, X11 // c44131f31b + VPSLLQ X2, X9, X11 // c46131f3da or c531f3da + VPSLLQ X11, X9, X11 // c44131f3db + VPSLLQ $7, X2, X9 // c4e13173f207 or c5b173f207 + VPSLLQ $7, X11, X9 // c4c13173f307 //TODO: VPSLLVD (BX), X9, X2 // c4e2314713 //TODO: VPSLLVD (R11), X9, X2 // c4c2314713 //TODO: VPSLLVD X2, X9, X2 // c4e23147d2 @@ -9738,30 +9738,30 @@ TEXT asmtest(SB),7,$0 //TODO: VPSRAW X11, Y15, Y11 // c44105e1db //TODO: VPSRAW $7, Y2, Y15 // c4e10571e207 or c58571e207 //TODO: VPSRAW $7, Y11, Y15 // c4c10571e307 - //TODO: VPSRLD (BX), X9, X2 // c4e131d213 or c5b1d213 - //TODO: VPSRLD (R11), X9, X2 // c4c131d213 - //TODO: VPSRLD X2, X9, X2 // c4e131d2d2 or c5b1d2d2 - //TODO: VPSRLD X11, X9, X2 // c4c131d2d3 - //TODO: VPSRLD (BX), X9, X11 // c46131d21b or c531d21b - //TODO: VPSRLD (R11), X9, X11 // c44131d21b - //TODO: VPSRLD X2, X9, X11 // c46131d2da or c531d2da - //TODO: VPSRLD X11, X9, X11 // c44131d2db - //TODO: VPSRLD $7, X2, X9 // c4e13172d207 or c5b172d207 - //TODO: VPSRLD $7, X11, X9 // c4c13172d307 - //TODO: VPSRLDQ $7, X2, X9 // c4e13173da07 or c5b173da07 - //TODO: VPSRLDQ $7, X11, X9 // c4c13173db07 - //TODO: VPSRLDQ $7, Y2, Y15 // c4e10573da07 or c58573da07 - //TODO: VPSRLDQ $7, Y11, Y15 // c4c10573db07 - //TODO: VPSRLQ (BX), X9, X2 // c4e131d313 or c5b1d313 - //TODO: VPSRLQ (R11), X9, X2 // c4c131d313 - //TODO: VPSRLQ X2, X9, X2 // c4e131d3d2 or c5b1d3d2 - //TODO: VPSRLQ X11, X9, X2 // c4c131d3d3 - //TODO: VPSRLQ (BX), X9, X11 // c46131d31b or c531d31b - //TODO: VPSRLQ (R11), X9, X11 // c44131d31b - //TODO: VPSRLQ X2, X9, X11 // c46131d3da or c531d3da - //TODO: VPSRLQ X11, X9, X11 // c44131d3db - //TODO: VPSRLQ $7, X2, X9 // c4e13173d207 or c5b173d207 - //TODO: VPSRLQ $7, X11, X9 // c4c13173d307 + VPSRLD (BX), X9, X2 // c4e131d213 or c5b1d213 + VPSRLD (R11), X9, X2 // c4c131d213 + VPSRLD X2, X9, X2 // c4e131d2d2 or c5b1d2d2 + VPSRLD X11, X9, X2 // c4c131d2d3 + VPSRLD (BX), X9, X11 // c46131d21b or c531d21b + VPSRLD (R11), X9, X11 // c44131d21b + VPSRLD X2, X9, X11 // c46131d2da or c531d2da + VPSRLD X11, X9, X11 // c44131d2db + VPSRLD $7, X2, X9 // c4e13172d207 or c5b172d207 + VPSRLD $7, X11, X9 // c4c13172d307 + VPSRLDQ $7, X2, X9 // c4e13173da07 or c5b173da07 + VPSRLDQ $7, X11, X9 // c4c13173db07 + VPSRLDQ $7, Y2, Y15 // c4e10573da07 or c58573da07 + VPSRLDQ $7, Y11, Y15 // c4c10573db07 + VPSRLQ (BX), X9, X2 // c4e131d313 or c5b1d313 + VPSRLQ (R11), X9, X2 // c4c131d313 + VPSRLQ X2, X9, X2 // c4e131d3d2 or c5b1d3d2 + VPSRLQ X11, X9, X2 // c4c131d3d3 + VPSRLQ (BX), X9, X11 // c46131d31b or c531d31b + VPSRLQ (R11), X9, X11 // c44131d31b + VPSRLQ X2, X9, X11 // c46131d3da or c531d3da + VPSRLQ X11, X9, X11 // c44131d3db + VPSRLQ $7, X2, X9 // c4e13173d207 or c5b173d207 + VPSRLQ $7, X11, X9 // c4c13173d307 //TODO: VPSRLVD (BX), X9, X2 // c4e2314513 //TODO: VPSRLVD (R11), X9, X2 // c4c2314513 //TODO: VPSRLVD X2, X9, X2 // c4e23145d2 diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 3ee4461352c30d..451798244f6db2 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -1089,6 +1089,8 @@ func (p *Package) gccMachine() []string { return []string{"-m31"} case "s390x": return []string{"-m64"} + case "mips64", "mips64le": + return []string{"-mabi=64"} } return nil } @@ -1241,12 +1243,20 @@ func (p *Package) gccErrors(stdin []byte) string { // TODO(rsc): require failure args := p.gccCmd() + // Optimization options can confuse the error messages; remove them. + nargs := make([]string, 0, len(args)) + for _, arg := range args { + if !strings.HasPrefix(arg, "-O") { + nargs = append(nargs, arg) + } + } + if *debugGcc { - fmt.Fprintf(os.Stderr, "$ %s <\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n") fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *, int, __SIZE_TYPE__), void *, int, __SIZE_TYPE__);\n") fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done();\n") fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n") + fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);") fmt.Fprintf(fgcc, "%s\n", tsanProlog) for _, exp := range p.ExpFunc { @@ -817,6 +831,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcch, "\nextern %s;\n", s) fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD") fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n") @@ -1020,7 +1035,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName) fmt.Fprint(fgcc, "\n") - fmt.Fprint(fgcc, "\n") + fmt.Fprint(fgcc, "\nCGO_NO_SANITIZE_THREAD\n") fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams) if resultCount > 0 { fmt.Fprintf(fgcc, "\t%s r;\n", cRet) @@ -1304,11 +1319,14 @@ extern char* _cgo_topofstack(void); // Prologue defining TSAN functions in C. const noTsanProlog = ` +#define CGO_NO_SANITIZE_THREAD #define _cgo_tsan_acquire() #define _cgo_tsan_release() ` const yesTsanProlog = ` +#define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread)) + long long _cgo_sync __attribute__ ((common)); extern void __tsan_acquire(void*); @@ -1346,9 +1364,6 @@ const goProlog = ` //go:linkname _cgo_runtime_cgocall runtime.cgocall func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32 -//go:linkname _cgo_runtime_cmalloc runtime.cmalloc -func _cgo_runtime_cmalloc(uintptr) unsafe.Pointer - //go:linkname _cgo_runtime_cgocallback runtime.cgocallback func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr) @@ -1360,10 +1375,8 @@ func _cgoCheckResult(interface{}) ` const gccgoGoProlog = ` -//extern runtime.cgoCheckPointer func _cgoCheckPointer(interface{}, ...interface{}) interface{} -//extern runtime.cgoCheckResult func _cgoCheckResult(interface{}) ` @@ -1396,7 +1409,7 @@ func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte { const cStringDef = ` func _Cfunc_CString(s string) *_Ctype_char { - p := _cgo_runtime_cmalloc(uintptr(len(s)+1)) + p := _cgo_cmalloc(uint64(len(s)+1)) pp := (*[1<<30]byte)(p) copy(pp[:], s) pp[len(s)] = 0 @@ -1406,7 +1419,7 @@ func _Cfunc_CString(s string) *_Ctype_char { const cBytesDef = ` func _Cfunc_CBytes(b []byte) unsafe.Pointer { - p := _cgo_runtime_cmalloc(uintptr(len(b))) + p := _cgo_cmalloc(uint64(len(b))) pp := (*[1<<30]byte)(p) copy(pp[:], b) return p @@ -1415,7 +1428,7 @@ func _Cfunc_CBytes(b []byte) unsafe.Pointer { const cMallocDef = ` func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer { - return _cgo_runtime_cmalloc(uintptr(n)) + return _cgo_cmalloc(uint64(n)) } ` @@ -1428,6 +1441,50 @@ var builtinDefs = map[string]string{ "_CMalloc": cMallocDef, } +// Definitions for C.malloc in Go and in C. We define it ourselves +// since we call it from functions we define, such as C.CString. +// Also, we have historically ensured that C.malloc does not return +// nil even for an allocation of 0. + +const cMallocDefGo = ` +//go:cgo_import_static _cgoPREFIX_Cfunc__Cmalloc +//go:linkname __cgofn__cgoPREFIX_Cfunc__Cmalloc _cgoPREFIX_Cfunc__Cmalloc +var __cgofn__cgoPREFIX_Cfunc__Cmalloc byte +var _cgoPREFIX_Cfunc__Cmalloc = unsafe.Pointer(&__cgofn__cgoPREFIX_Cfunc__Cmalloc) + +//go:cgo_unsafe_args +func _cgo_cmalloc(p0 uint64) (r1 unsafe.Pointer) { + _cgo_runtime_cgocall(_cgoPREFIX_Cfunc__Cmalloc, uintptr(unsafe.Pointer(&p0))) + return +} +` + +// cMallocDefC defines the C version of C.malloc for the gc compiler. +// It is defined here because C.CString and friends need a definition. +// We define it by hand, rather than simply inventing a reference to +// C.malloc, because may not have been included. +// This is approximately what writeOutputFunc would generate, but +// skips the cgo_topofstack code (which is only needed if the C code +// calls back into Go). This also avoids returning nil for an +// allocation of 0 bytes. +const cMallocDefC = ` +CGO_NO_SANITIZE_THREAD +void _cgoPREFIX_Cfunc__Cmalloc(void *v) { + struct { + unsigned long long p0; + void *r1; + } PACKED *a = v; + void *ret; + _cgo_tsan_acquire(); + ret = malloc(a->p0); + if (ret == 0 && a->p0 == 0) { + ret = malloc(1); + } + a->r1 = ret; + _cgo_tsan_release(); +} +` + func (p *Package) cPrologGccgo() string { return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1), "GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1) diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go index 2b45e5b998bc10..2e77f702e353f5 100644 --- a/src/cmd/compile/doc.go +++ b/src/cmd/compile/doc.go @@ -60,8 +60,15 @@ Flags: -installsuffix suffix Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix instead of $GOROOT/pkg/$GOOS_$GOARCH. + -l + Disable inlining. -largemodel - Generated code that assumes a large memory model. + Generate code that assumes a large memory model. + -linkobj file + Write linker-specific object to file and compiler-specific + object to usual output file (as specified by -o). + Without this flag, the -o output is a combination of both + linker and compiler input. -memprofile file Write memory profile for the compilation to file. -memprofilerate rate diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go index 461ef2ada1bb32..42915340a06bf8 100644 --- a/src/cmd/compile/internal/amd64/galign.go +++ b/src/cmd/compile/internal/amd64/galign.go @@ -25,18 +25,16 @@ func betypeinit() { cmpptr = x86.ACMPL } - if gc.Ctxt.Flag_dynlink { - gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15) + if gc.Ctxt.Flag_dynlink || obj.Getgoos() == "nacl" { + resvd = append(resvd, x86.REG_R15) } -} - -func Main() { - if obj.Getgoos() == "nacl" { - resvd = append(resvd, x86.REG_BP, x86.REG_R15) - } else if obj.Framepointer_enabled != 0 { + if gc.Ctxt.Framepointer_enabled || obj.Getgoos() == "nacl" { resvd = append(resvd, x86.REG_BP) } + gc.Thearch.ReservedRegs = resvd +} +func Main() { gc.Thearch.LinkArch = &x86.Linkamd64 if obj.Getgoarch() == "amd64p32" { gc.Thearch.LinkArch = &x86.Linkamd64p32 @@ -51,7 +49,6 @@ func Main() { gc.Thearch.FREGMIN = x86.REG_X0 gc.Thearch.FREGMAX = x86.REG_X15 gc.Thearch.MAXWIDTH = 1 << 50 - gc.Thearch.ReservedRegs = resvd gc.Thearch.AddIndex = addindex gc.Thearch.Betypeinit = betypeinit diff --git a/src/cmd/compile/internal/amd64/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go index f862e8a92ba9bb..5d9070ca130c0d 100644 --- a/src/cmd/compile/internal/amd64/gsubr.go +++ b/src/cmd/compile/internal/amd64/gsubr.go @@ -116,7 +116,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { base = n1.Left } - if base.Op == gc.ONAME && base.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG { + if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG { r1 = *n1 } else { gc.Regalloc(&r1, t, n1) @@ -229,6 +229,8 @@ func gmove(f *gc.Node, t *gc.Node) { switch uint32(ft)<<16 | uint32(tt) { default: + gc.Dump("f", f) + gc.Dump("t", t) gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong)) /* diff --git a/src/cmd/compile/internal/amd64/reg.go b/src/cmd/compile/internal/amd64/reg.go index 764f5c3a9e6380..77720c855f13f1 100644 --- a/src/cmd/compile/internal/amd64/reg.go +++ b/src/cmd/compile/internal/amd64/reg.go @@ -32,7 +32,6 @@ package amd64 import ( "cmd/compile/internal/gc" - "cmd/internal/obj" "cmd/internal/obj/x86" ) @@ -121,7 +120,7 @@ func BtoR(b uint64) int { b &= 0xffff if gc.Nacl { b &^= (1<<(x86.REG_BP-x86.REG_AX) | 1<<(x86.REG_R15-x86.REG_AX)) - } else if obj.Framepointer_enabled != 0 { + } else if gc.Ctxt.Framepointer_enabled { // BP is part of the calling convention if framepointer_enabled. b &^= (1 << (x86.REG_BP - x86.REG_AX)) } diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index 54d878d92bd0af..756bcec75c88ee 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -878,6 +878,18 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { gc.Gvarkill(v.Aux.(*gc.Node)) case ssa.OpVarLive: gc.Gvarlive(v.Aux.(*gc.Node)) + case ssa.OpKeepAlive: + if !v.Args[0].Type.IsPtrShaped() { + v.Fatalf("keeping non-pointer alive %v", v.Args[0]) + } + n, off := gc.AutoVar(v.Args[0]) + if n == nil { + v.Fatalf("KeepLive with non-spilled value %s %s", v, v.Args[0]) + } + if off != 0 { + v.Fatalf("KeepLive with non-zero offset spill location %s:%d", n, off) + } + gc.Gvarlive(n) case ssa.OpAMD64LoweredNilCheck: // Optimization - if the subsequent block has a load or store // at the same address, we don't need to issue this instruction. diff --git a/src/cmd/compile/internal/arm/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go index 9ac999167e1dbb..b5d7bc05c405e4 100644 --- a/src/cmd/compile/internal/arm/gsubr.go +++ b/src/cmd/compile/internal/arm/gsubr.go @@ -86,17 +86,8 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { n = &n1 - case gc.ONAME: - if n.Class == gc.PPARAMREF { - var n1 gc.Node - gc.Cgen(n.Name.Heapaddr, &n1) - sclean[nsclean-1] = n1 - n = &n1 - } - + case gc.ONAME, gc.OINDREG: // nothing - case gc.OINDREG: - break } *lo = *n diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index 81230413182f4c..2b62405544d697 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -55,12 +55,15 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 { } f.Offset = o if f.Nname != nil { - // this same stackparam logic is in addrescapes - // in typecheck.go. usually addrescapes runs after - // widstruct, in which case we could drop this, + // addrescapes has similar code to update these offsets. + // Usually addrescapes runs after widstruct, + // in which case we could drop this, // but function closure functions are the exception. - if f.Nname.Name.Param.Stackparam != nil { - f.Nname.Name.Param.Stackparam.Xoffset = o + // NOTE(rsc): This comment may be stale. + // It's possible the ordering has changed and this is + // now the common case. I'm not sure. + if f.Nname.Name.Param.Stackcopy != nil { + f.Nname.Name.Param.Stackcopy.Xoffset = o f.Nname.Xoffset = 0 } else { f.Nname.Xoffset = o diff --git a/src/cmd/compile/internal/gc/asm_test.go b/src/cmd/compile/internal/gc/asm_test.go new file mode 100644 index 00000000000000..469f0864d5c35f --- /dev/null +++ b/src/cmd/compile/internal/gc/asm_test.go @@ -0,0 +1,105 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gc + +import ( + "bytes" + "fmt" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strings" + "testing" +) + +// TestAssembly checks to make sure the assembly generated for +// functions contains certain expected instructions. +// Note: this test will fail if -ssa=0. +func TestAssembly(t *testing.T) { + testenv.MustHaveGoBuild(t) + if runtime.GOOS == "windows" { + // TODO: remove if we can get "go tool compile -S" to work on windows. + t.Skipf("skipping test: recursive windows compile not working") + } + dir, err := ioutil.TempDir("", "TestAssembly") + if err != nil { + t.Fatalf("could not create directory: %v", err) + } + defer os.RemoveAll(dir) + + for _, test := range asmTests { + asm := compileToAsm(dir, test.arch, fmt.Sprintf(template, test.function)) + // Get rid of code for "".init. Also gets rid of type algorithms & other junk. + if i := strings.Index(asm, "\n\"\".init "); i >= 0 { + asm = asm[:i+1] + } + for _, r := range test.regexps { + if b, err := regexp.MatchString(r, asm); !b || err != nil { + t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, test.function, asm) + } + } + } +} + +// compile compiles the package pkg for architecture arch and +// returns the generated assembly. dir is a scratch directory. +func compileToAsm(dir, arch, pkg string) string { + // Create source. + src := filepath.Join(dir, "test.go") + f, err := os.Create(src) + if err != nil { + panic(err) + } + f.Write([]byte(pkg)) + f.Close() + + var stdout, stderr bytes.Buffer + cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src) + cmd.Env = append(cmd.Env, "GOARCH="+arch) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + panic(err) + } + if s := stderr.String(); s != "" { + panic(fmt.Errorf("Stderr = %s\nWant empty", s)) + } + return stdout.String() +} + +// template to convert a function to a full file +const template = ` +package main +%s +` + +type asmTest struct { + // architecture to compile to + arch string + // function to compile + function string + // regexps that must match the generated assembly + regexps []string +} + +var asmTests = [...]asmTest{ + {"amd64", ` +func f(x int) int { + return x * 64 +} +`, + []string{"\tSHLQ\t\\$6,"}, + }, + {"amd64", ` +func f(x int) int { + return x * 96 +}`, + []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"}, + }, +} diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 5d037ae05e74d1..f533053cd74acc 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -6,7 +6,7 @@ // (see fmt.go, parser.go as "documentation" for how to use/setup data structures) /* -Export data encoding: +1) Export data encoding principles: The export data is a serialized description of the graph of exported "objects": constants, types, variables, and functions. In general, @@ -49,7 +49,7 @@ Before exporting or importing, the type tables are populated with the predeclared types (int, string, error, unsafe.Pointer, etc.). This way they are automatically encoded with a known and fixed type index. -Encoding format: +2) Encoding format: The export data starts with a single byte indicating the encoding format (compact, or with debugging information), followed by a version string @@ -84,6 +84,43 @@ each encoding routine there is a matching and symmetric decoding routine. This symmetry makes it very easy to change or extend the format: If a new field needs to be encoded, a symmetric change can be made to exporter and importer. + +3) Making changes to the encoding format: + +Any change to the encoding format requires a respective change in the +exporter below and a corresponding symmetric change to the importer in +bimport.go. + +Furthermore, it requires a corresponding change to go/internal/gcimporter +and golang.org/x/tools/go/gcimporter15. Changes to the latter must preserve +compatibility with both the last release of the compiler, and with the +corresponding compiler at tip. That change is necessarily more involved, +as it must switch based on the version number in the export data file. + +It is recommended to turn on debugFormat when working on format changes +as it will help finding encoding/decoding inconsistencies quickly. + +Special care must be taken to update builtin.go when the export format +changes: builtin.go contains the export data obtained by compiling the +builtin/runtime.go and builtin/unsafe.go files; those compilations in +turn depend on importing the data in builtin.go. Thus, when the export +data format changes, the compiler must be able to import the data in +builtin.go even if its format has not yet changed. Proceed in several +steps as follows: + +- Change the exporter to use the new format, and use a different version + string as well. +- Update the importer accordingly, but accept both the old and the new + format depending on the version string. +- all.bash should pass at this point. +- Run mkbuiltin.go: this will create a new builtin.go using the new + export format. +- go test -run Builtin should pass at this point. +- Remove importer support for the old export format and (maybe) revert + the version string again (it's only needed to mark the transition). +- all.bash should still pass. + +Don't forget to set debugFormat to false. */ package gc @@ -125,6 +162,17 @@ const exportVersion = "v0" // Leave for debugging. const exportInlined = true // default: true +// trackAllTypes enables cycle tracking for all types, not just named +// types. The existing compiler invariants assume that unnamed types +// that are not completely set up are not used, or else there are spurious +// errors. +// If disabled, only named types are tracked, possibly leading to slightly +// less efficient encoding in rare cases. It also prevents the export of +// some corner-case type declarations (but those are not handled correctly +// with with the textual export format either). +// TODO(gri) enable and remove once issues caused by it are fixed +const trackAllTypes = false + type exporter struct { out *bufio.Writer @@ -159,6 +207,10 @@ func export(out *bufio.Writer, trace bool) int { trace: trace, } + // TODO(gri) clean up the ad-hoc encoding of the file format below + // (we need this so we can read the builtin package export data + // easily w/o being affected by format changes) + // first byte indicates low-level encoding format var format byte = 'c' // compact if debugFormat { @@ -166,6 +218,12 @@ func export(out *bufio.Writer, trace bool) int { } p.rawByte(format) + format = 'n' // track named types only + if trackAllTypes { + format = 'a' + } + p.rawByte(format) + // posInfo exported or not? p.bool(p.posInfoFormat) @@ -585,14 +643,21 @@ func (p *exporter) typ(t *Type) { } // otherwise, remember the type, write the type tag (< 0) and type data - if p.trace { - p.tracef("T%d = {>\n", len(p.typIndex)) - defer p.tracef("<\n} ") + if trackAllTypes { + if p.trace { + p.tracef("T%d = {>\n", len(p.typIndex)) + defer p.tracef("<\n} ") + } + p.typIndex[t] = len(p.typIndex) } - p.typIndex[t] = len(p.typIndex) // pick off named types if tsym := t.Sym; tsym != nil { + if !trackAllTypes { + // if we don't track all types, track named types now + p.typIndex[t] = len(p.typIndex) + } + // Predeclared types should have been found in the type map. if t.Orig == t { Fatalf("exporter: predeclared type missing from type map?") @@ -909,7 +974,7 @@ func parName(f *Field, numbered bool) string { // print symbol with Vargen number or not as desired name := s.Name if strings.Contains(name, ".") { - panic("invalid symbol name: " + name) + Fatalf("invalid symbol name: %s", name) } // Functions that can be inlined use numbered parameters so we can distingish them @@ -1012,6 +1077,8 @@ func (p *exporter) float(x *Mpflt) { // but instead of emitting the information textually, emit the node tree in // binary form. +// TODO(gri) Improve tracing output. The current format is difficult to read. + // stmtList may emit more (or fewer) than len(list) nodes. func (p *exporter) stmtList(list Nodes) { if p.trace { @@ -1088,6 +1155,16 @@ func (p *exporter) expr(n *Node) { defer p.tracef(") ") } + // from nodefmt (fmt.go) + // + // nodefmt reverts nodes back to their original - we don't need to do + // it because we are not bound to produce valid Go syntax when exporting + // + // if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil { + // n = n.Orig + // } + + // from exprfmt (fmt.go) for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) { n = n.Left } @@ -1117,15 +1194,13 @@ func (p *exporter) expr(n *Node) { // Special case: name used as local variable in export. // _ becomes ~b%d internally; print as _ for export if n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' { - // case 0: mapped to ONAME p.op(ONAME) - p.bool(true) // indicate blank identifier + p.string("_") // inlined and customized version of p.sym(n) break } if n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 { - // case 1: mapped to OPACK - p.op(OPACK) + p.op(ONAME) p.sym(n) break } @@ -1134,24 +1209,18 @@ func (p *exporter) expr(n *Node) { // but for export, this should be rendered as (*pkg.T).meth. // These nodes have the special property that they are names with a left OTYPE and a right ONAME. if n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME { - // case 2: mapped to ONAME - p.op(ONAME) - // TODO(gri) can we map this case directly to OXDOT - // and then get rid of the bool here? - p.bool(false) // indicate non-blank identifier - p.typ(n.Left.Type) + p.op(OXDOT) + p.expr(n.Left) // n.Left.Op == OTYPE p.fieldSym(n.Right.Sym, true) break } - // case 3: mapped to OPACK - p.op(OPACK) - p.sym(n) // fallthrough inlined here - - case OPACK, ONONAME: - p.op(op) + p.op(ONAME) p.sym(n) + // case OPACK, ONONAME: + // should have been resolved by typechecking - handled by default case + case OTYPE: p.op(OTYPE) if p.bool(n.Type == nil) { @@ -1160,14 +1229,14 @@ func (p *exporter) expr(n *Node) { p.typ(n.Type) } - case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: - panic("unreachable") // should have been resolved by typechecking + // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC: + // should have been resolved by typechecking - handled by default case // case OCLOSURE: // unimplemented - handled by default case // case OCOMPLIT: - // unimplemented - handled by default case + // should have been resolved by typechecking - handled by default case case OPTRLIT: p.op(OPTRLIT) @@ -1176,16 +1245,12 @@ func (p *exporter) expr(n *Node) { case OSTRUCTLIT: p.op(OSTRUCTLIT) - if !p.bool(n.Implicit) { - p.typ(n.Type) - } + p.typ(n.Type) p.elemList(n.List) // special handling of field names case OARRAYLIT, OMAPLIT: - p.op(op) - if !p.bool(n.Implicit) { - p.typ(n.Type) - } + p.op(OCOMPLIT) + p.typ(n.Type) p.exprList(n.List) case OKEY: @@ -1198,9 +1263,6 @@ func (p *exporter) expr(n *Node) { case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: p.op(OXDOT) p.expr(n.Left) - if n.Sym == nil { - panic("unreachable") // can this happen during export? - } p.fieldSym(n.Sym, true) case ODOTTYPE, ODOTTYPE2: @@ -1231,26 +1293,35 @@ func (p *exporter) expr(n *Node) { p.expr(max) case OCOPY, OCOMPLEX: + // treated like other builtin calls (see e.g., OREAL) p.op(op) p.expr(n.Left) p.expr(n.Right) + p.op(OEND) case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR: p.op(OCONV) p.typ(n.Type) - if p.bool(n.Left != nil) { + if n.Left != nil { p.expr(n.Left) + p.op(OEND) } else { - p.exprList(n.List) + p.exprList(n.List) // emits terminating OEND } case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: p.op(op) - if p.bool(n.Left != nil) { + if n.Left != nil { p.expr(n.Left) + p.op(OEND) } else { - p.exprList(n.List) + p.exprList(n.List) // emits terminating OEND + } + // only append() calls may contain '...' arguments + if op == OAPPEND { p.bool(n.Isddd) + } else if n.Isddd { + Fatalf("exporter: unexpected '...' with %s call", opnames[op]) } case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG: @@ -1306,7 +1377,8 @@ func (p *exporter) expr(n *Node) { p.op(ODCLCONST) default: - Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", n.Op) + Fatalf("cannot export %s (%d) node\n"+ + "==> please file an issue and assign to gri@\n", n.Op, n.Op) } } @@ -1336,8 +1408,8 @@ func (p *exporter) stmt(n *Node) { switch op := n.Op; op { case ODCL: p.op(ODCL) - switch n.Left.Class &^ PHEAP { - case PPARAM, PPARAMOUT, PAUTO: + switch n.Left.Class { + case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP: // TODO(gri) when is this not PAUTO? // Also, originally this didn't look like // the default case. Investigate. @@ -1370,10 +1442,7 @@ func (p *exporter) stmt(n *Node) { p.expr(n.Right) } - case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: - fallthrough - - case OAS2: + case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: p.op(OAS2) p.exprList(n.List) p.exprList(n.Rlist) @@ -1382,9 +1451,8 @@ func (p *exporter) stmt(n *Node) { p.op(ORETURN) p.exprList(n.List) - case ORETJMP: - // generated by compiler for trampolin routines - not exported - panic("unreachable") + // case ORETJMP: + // unreachable - generated by compiler for trampolin routines case OPROC, ODEFER: p.op(op) @@ -1420,19 +1488,18 @@ func (p *exporter) stmt(n *Node) { p.stmtList(n.List) p.stmtList(n.Nbody) - case OFALL: - op = OXFALL - fallthrough + case OFALL, OXFALL: + p.op(OXFALL) - case OBREAK, OCONTINUE, OGOTO, OXFALL: + case OBREAK, OCONTINUE: p.op(op) p.exprsOrNil(n.Left, nil) case OEMPTY: - // nothing to emit + // nothing to emit - case OLABEL: - p.op(OLABEL) + case OGOTO, OLABEL: + p.op(op) p.expr(n.Left) default: @@ -1475,6 +1542,8 @@ func (p *exporter) fieldSym(s *Sym, short bool) { } } +// sym must encode the _ (blank) identifier as a single string "_" since +// encoding for some nodes is based on this assumption (e.g. ONAME nodes). func (p *exporter) sym(n *Node) { s := n.Sym if s.Pkg != nil { diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 6b0593cd472fc4..36aa0e8b9ceaf3 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. // Binary package import. -// Based loosely on x/tools/go/importer. +// See bexport.go for the export data format and how +// to make a format change. package gc @@ -24,10 +25,11 @@ type importer struct { buf []byte // reused for reading strings // object lists, in order of deserialization - strList []string - pkgList []*Pkg - typList []*Type - funcList []*Node // nil entry means already declared + strList []string + pkgList []*Pkg + typList []*Type + funcList []*Node // nil entry means already declared + trackAllTypes bool // for delayed type verification cmpList []struct{ pt, t *Type } @@ -59,6 +61,8 @@ func Import(in *bufio.Reader) { Fatalf("importer: invalid encoding format in export data: got %q; want 'c' or 'd'", format) } + p.trackAllTypes = p.rawByte() == 'a' + p.posInfoFormat = p.bool() // --- generic export data --- @@ -100,7 +104,9 @@ func Import(in *bufio.Reader) { // --- compiler-specific export data --- // read compiler-specific flags - importpkg.Safe = p.bool() + + // read but ignore safemode bit (see issue #15772) + p.bool() // formerly: importpkg.Safe = p.bool() // phase 2 objcount = 0 @@ -230,7 +236,7 @@ func (p *importer) pkg() *Pkg { // an empty path denotes the package we are currently importing; // it must be the first package we see if (path == "") != (len(p.pkgList) == 0) { - panic(fmt.Sprintf("package path %q for pkg index %d", path, len(p.pkgList))) + Fatalf("importer: package path %q for pkg index %d", path, len(p.pkgList)) } pkg := importpkg @@ -331,7 +337,9 @@ func (p *importer) pos() { func (p *importer) newtyp(etype EType) *Type { t := typ(etype) - p.typList = append(p.typList, t) + if p.trackAllTypes { + p.typList = append(p.typList, t) + } return t } @@ -389,7 +397,13 @@ func (p *importer) typ() *Type { // read underlying type // parser.go:hidden_type t0 := p.typ() - p.importtype(t, t0) // parser.go:hidden_import + if p.trackAllTypes { + // If we track all types, we cannot check equality of previously + // imported types until later. Use customized version of importtype. + p.importtype(t, t0) + } else { + importtype(t, t0) + } // interfaces don't have associated methods if t0.IsInterface() { @@ -788,16 +802,11 @@ func (p *importer) node() *Node { return n case ONAME: - if p.bool() { - // "_" - // TODO(gri) avoid repeated "_" lookup - return mkname(Pkglookup("_", localpkg)) - } - return NodSym(OXDOT, typenod(p.typ()), p.fieldSym()) - - case OPACK, ONONAME: return mkname(p.sym()) + // case OPACK, ONONAME: + // unreachable - should have been resolved by typechecking + case OTYPE: if p.bool() { return mkname(p.sym()) @@ -810,12 +819,9 @@ func (p *importer) node() *Node { // case OCLOSURE: // unimplemented - // case OCOMPLIT: - // unimplemented - case OPTRLIT: n := p.expr() - if !p.bool() /* !implicit, i.e. '&' operator*/ { + if !p.bool() /* !implicit, i.e. '&' operator */ { if n.Op == OCOMPLIT { // Special case for &T{...}: turn into (*T){...}. n.Right = Nod(OIND, n.Right, nil) @@ -827,18 +833,15 @@ func (p *importer) node() *Node { return n case OSTRUCTLIT: - n := Nod(OCOMPLIT, nil, nil) - if !p.bool() { - n.Right = typenod(p.typ()) - } - n.List.Set(p.elemList()) + n := Nod(OCOMPLIT, nil, typenod(p.typ())) + n.List.Set(p.elemList()) // special handling of field names return n - case OARRAYLIT, OMAPLIT: - n := Nod(OCOMPLIT, nil, nil) - if !p.bool() { - n.Right = typenod(p.typ()) - } + // case OARRAYLIT, OMAPLIT: + // unreachable - mapped to case OCOMPLIT below by exporter + + case OCOMPLIT: + n := Nod(OCOMPLIT, nil, typenod(p.typ())) n.List.Set(p.exprList()) return n @@ -854,14 +857,7 @@ func (p *importer) node() *Node { case OXDOT: // see parser.new_dotname - obj := p.expr() - sel := p.fieldSym() - if obj.Op == OPACK { - s := restrictlookup(sel.Name, obj.Name.Pkg) - obj.Used = true - return oldname(s) - } - return NodSym(OXDOT, obj, sel) + return NodSym(OXDOT, p.expr(), p.fieldSym()) // case ODOTTYPE, ODOTTYPE2: // unreachable - mapped to case ODOTTYPE below by exporter @@ -891,29 +887,18 @@ func (p *importer) node() *Node { n.SetSliceBounds(low, high, max) return n - case OCOPY, OCOMPLEX: - n := builtinCall(op) - n.List.Set([]*Node{p.expr(), p.expr()}) - return n - // case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR: // unreachable - mapped to OCONV case below by exporter case OCONV: n := Nod(OCALL, typenod(p.typ()), nil) - if p.bool() { - n.List.Set1(p.expr()) - } else { - n.List.Set(p.exprList()) - } + n.List.Set(p.exprList()) return n - case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: + case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: n := builtinCall(op) - if p.bool() { - n.List.Set1(p.expr()) - } else { - n.List.Set(p.exprList()) + n.List.Set(p.exprList()) + if op == OAPPEND { n.Isddd = p.bool() } return n @@ -1053,6 +1038,7 @@ func (p *importer) node() *Node { case OXCASE: markdcl() n := Nod(OXCASE, nil, nil) + n.Xoffset = int64(block) n.List.Set(p.exprList()) // TODO(gri) eventually we must declare variables for type switch // statements (type switch statements are not yet exported) @@ -1063,23 +1049,32 @@ func (p *importer) node() *Node { // case OFALL: // unreachable - mapped to OXFALL case below by exporter - case OBREAK, OCONTINUE, OGOTO, OXFALL: + case OXFALL: + n := Nod(OXFALL, nil, nil) + n.Xoffset = int64(block) + return n + + case OBREAK, OCONTINUE: left, _ := p.exprsOrNil() + if left != nil { + left = newname(left.Sym) + } return Nod(op, left, nil) // case OEMPTY: // unreachable - not emitted by exporter - case OLABEL: - n := Nod(OLABEL, p.expr(), nil) - n.Left.Sym = dclstack // context, for goto restrictions + case OGOTO, OLABEL: + n := Nod(op, newname(p.expr().Sym), nil) + n.Sym = dclstack // context, for goto restrictions return n case OEND: return nil default: - Fatalf("importer: %s (%d) node not yet supported", op, op) + Fatalf("cannot import %s (%d) node\n"+ + "==> please file an issue and assign to gri@\n", op, op) panic("unreachable") // satisfy compiler } } diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go index cc64e73f25c1e3..b9010f4366dcce 100644 --- a/src/cmd/compile/internal/gc/builtin.go +++ b/src/cmd/compile/internal/gc/builtin.go @@ -3,106 +3,108 @@ package gc const runtimeimport = "" + - "c\x00\x03v0\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00\x01" + - "\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15panic" + - "divide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00\t" + - "\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11gor" + - "ecover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13printf" + - "loat\x00\x01\x1a\x00\x00\t\x0fprintint\x00\x01\n\x00\x00\t\x0fprinthex\x00\x01\x14\x00\x00\t" + - "\x11printuint\x00\x01\x14\x00\x00\t\x17printcomplex\x00\x01\x1e\x00\x00\t\x15prin" + - "tstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printif" + - "ace\x00\x01:\x00\x00\t\x13printeface\x00\x01:\x00\x00\t\x13printslice\x00\x01:" + - "\x00\x00\t\rprintnl\x00\x00\x00\t\rprintsp\x00\x00\x00\t\x11printlock\x00\x00\x00" + - "\t\x15printunlock\x00\x00\x00\t\x19concatstring2\x00\x05\x17\x0f@\"\x00 \x00" + - " \x00\x01 \x00\t\x19concatstring3\x00\a\x17\x0f@\"\x00 \x00 \x00 \x00\x01 \x00\t\x19co" + - "ncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstr" + - "ing5\x00\v\x17\x0f@\"\x00 \x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstrings\x00" + - "\x03\x17\x0f@\"\x00\x11 \x00\x01 \x00\t\x11cmpstring\x00\x03 \x00 \x00\x01\x02\x00\t\x0feqstri" + - "ng\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!slic" + - "ebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebytetos" + - "tringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostring\x00\x03\x17\x0f@" + - "\"\x00\x11|S\x00\x01 \x00\t!stringtoslicebyte\x00\x03\x17\x0f@\"\x00 \x00\x01\x11\"" + - "\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t!stringt" + - "oslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x13stringiter\x00\x03 " + - "\x00\x02\x00\x01\x02\x00\t\x15stringiter2\x00\x03 \x00\x02\x00\x04\x02\rretk·1\x00\x00|S\r" + - "retv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00\x00" + - "\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dslicestring" + - "copy\x00\x04:^\x00\x00:`\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00\x02" + - ":\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\b\x00\x00:\relem·3\x00\x00\x02:l" + - "\x00\x00\t\rconvT2E\x00\x06\x17\"\b\x00\x00>p\x00\x00>\vbuf·4\x00\x00\x02:l\x00\x00\t\rc" + - "onvT2I\x00\x06\x17\"\vtab·2\x00\x00>p\x00\x00>t\x00\x00\x02:l\x00\x00\t\x11assert" + - "E2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00>\vret·3\x00\x00\x00\t" + - "\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00>\vret·4\x00\x00" + - "\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assert" + - "E2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertE2T\x00\x06\x17\"|" + - "|\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01" + - "\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13asse" + - "rtI2E2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2I\x00\x06\x17" + - "\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13assertI2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>" + - "\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"||\x00\x00:~\x00\x00>\x80\x01\x00\x00\x00\t\x13as" + - "sertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00>\x86\x01\x00\x00\x01\x00\x00\t\x17panicdotty" + - "pe\x00\x06\x17\"\rhave·1\x00\x00\x9a\x01\rwant·2\x00\x00\x9a\x01\x84\x01\x00\x00\x00\t\rifa" + - "ceeq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00\x00\x02\x00l\x00\x00\t\refaceeq\x00\x04" + - ":\xa4\x01\x00\x00:\xa6\x01\x00\x00\x02\x00l\x00\x00\t\rmakemap\x00\b\x17\"\x13mapType·2\x00" + - "\x00\n\rhint·3\x00\x00>\x11mapbuf·4\x00\x00>\x17bucketbuf·5\x00" + - "\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapaccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rh" + - "map·3\x00\x00>\vkey·4\x00\x00\x02>\vval·1\x00\x00\t!mapaccess" + - "1_fast32\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t!mapa" + - "ccess1_fast64\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02>\xbc\x01\x00\x00\t" + - "#mapaccess1_faststr\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02" + - ">\xbc\x01\x00\x00\t\x1bmapaccess1_fat\x00\b\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00>\xba\x01\x00" + - "\x00\x17\"\rzero·5\x00\x00\x02>\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapT" + - "ype·3\x00\x00\x1d::\rhmap·4\x00\x00>\vkey·5\x00\x00\x04>\xbc\x01\x00\x00\x00\rp" + - "res·2\x00\x00\t!mapaccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01" + - "\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17" + - "\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t#mapaccess2" + - "_faststr\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t" + - "\x1bmapaccess2_fat\x00\b\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00>\xce\x01\x00\x00\x17\"\rze" + - "ro·6\x00\x00\x04>\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapTy" + - "pe·1\x00\x00\x1d::\rhmap·2\x00\x00>\vkey·3\x00\x00>\vval·4\x00\x00" + - "\x00\t\x15mapiterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\x0fhiter·3\x00" + - "\x00\x00\t\x11mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00>\xe2\x01\x00\x00\x00\t\x15mapi" + - "ternext\x00\x02>\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15cha" + - "nType·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv" + - "1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00>p\x00\x00\x00\t\x11" + - "chanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00>\relem·4" + - "\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf8\x01\x00\x00\x1f\x04:\xfa\x01\x00\x00>p\x00\x00\x00\t\x11cl" + - "osechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renabled" + - "\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04>" + - "\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"||" + - "\x00\x00>\vdst·2\x00\x00>\vsrc·3\x00\x00\x00\t\x1btypedslicecopy\x00" + - "\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selectnbs" + - "end\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x01\x00\x00\t\x17selectnbrecv" + - "\x00\x06\x17\"\xf2\x01\x00\x00>p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19selectnbr" + - "ecv2\x00\b\x17\"\xf2\x01\x00\x00>p\x00\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhcha" + - "n·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsi" + - "ze·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel\xc2" + - "\xb72\x00\x00\x1f\x04:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13select" + - "recv\x00\x06\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00>\x80\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t\x15selectre" + - "cv2\x00\b\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00>\x80\x02\x00\x00\xf8\x01\x15received·5\x00\x00\x02" + - "\x00\xb8\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb6\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t\x0fsele" + - "ctgo\x00\x02\x17\"\xae\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\b\x00" + - "\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x11grows" + - "lice\x00\x06\x17\"\b\x00\x00\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:\xcc\x02\x00\x00\t\rmemm" + - "ove\x00\x06>\tto·1\x00\x00>\vfrm·2\x00\x00\x16\x11length·3\x00d\x00\t\v" + - "memclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length·2\x00d\x00\t\x0fmemeq" + - "ual\x00\x06>\ax·2\x00\x00>\ay·3\x00\x00\x16\rsize·4\x00d\x01\x00\x00\t\x11mem" + - "equal8\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04>\xe2\x02\x00\x00" + - ">\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x13mem" + - "equal64\x00\x04>\xe2\x02\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04>\xe2\x02" + - "\x00\x00>\xe4\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div" + - "\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00" + - "\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64" + - "touint64\x00\x01\x1a\x00\x01\x14\x00\t\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1d" + - "uint64tofloat64\x00\x01\x14\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e" + - "\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefunc" + - "enter\x00\x01\x16d\x00\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01\x16" + - "d\x00\t\x11racewrite\x00\x01\x16d\x00\t\x19racereadrange\x00\x04\x16\radd" + - "r·1\x00d\x16\rsize·2\x00d\x00\t\x1bracewriterange\x00\x04\x16\x94\x03\x00" + - "d\x16\x96\x03\x00d\x00\t\x0fmsanread\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x11msanwrit" + - "e\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\v\xf4\x01\x02\v\x00\x01\x00\n$$\n" + "cn\x00\x03v0\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00" + + "\x01\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15pani" + + "cdivide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00" + + "\t\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11go" + + "recover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13print" + + "float\x00\x01\x1a\x00\x00\t\x0fprintint\x00\x01\n\x00\x00\t\x0fprinthex\x00\x01\x14\x00\x00" + + "\t\x11printuint\x00\x01\x14\x00\x00\t\x17printcomplex\x00\x01\x1e\x00\x00\t\x15pri" + + "ntstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printi" + + "face\x00\x01:\x00\x00\t\x13printeface\x00\x01:\x00\x00\t\x13printslice\x00\x01" + + ":\x00\x00\t\rprintnl\x00\x00\x00\t\rprintsp\x00\x00\x00\t\x11printlock\x00\x00" + + "\x00\t\x15printunlock\x00\x00\x00\t\x19concatstring2\x00\x05\x17\x0f@\"\x00 " + + "\x00 \x00\x01 \x00\t\x19concatstring3\x00\a\x17\x0f@\"\x00 \x00 \x00 \x00\x01 \x00\t\x19c" + + "oncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatst" + + "ring5\x00\v\x17\x0f@\"\x00 \x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstrings" + + "\x00\x03\x17\x0f@\"\x00\x11 \x00\x01 \x00\t\x11cmpstring\x00\x03 \x00 \x00\x01\x02\x00\t\x0feqstr" + + "ing\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!sli" + + "cebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebyteto" + + "stringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostring\x00\x03\x17\x0f" + + "@\"\x00\x11|S\x00\x01 \x00\t!stringtoslicebyte\x00\x03\x17\x0f@\"\x00 \x00\x01\x11" + + "\"\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t!string" + + "toslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x13stringiter\x00\x03" + + " \x00\x02\x00\x01\x02\x00\t\x15stringiter2\x00\x03 \x00\x02\x00\x04\x02\rretk·1\x00\x00|S" + + "\rretv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00" + + "\x00\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dslicestrin" + + "gcopy\x00\x04:^\x00\x00:`\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00" + + "\x02:\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\b\x00\x00:\relem·3\x00\x00\x02:" + + "l\x00\x00\t\rconvT2E\x00\x06\x17\"\b\x00\x00\x17:p\x00\x00\x17:\vbuf·4\x00\x00\x02:l\x00\x00" + + "\t\rconvT2I\x00\x06\x17\"\vtab·2\x00\x00\x17:p\x00\x00\x17:t\x00\x00\x02:l\x00\x00\t\x11a" + + "ssertE2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00\x17:\vret\xc2" + + "\xb73\x00\x00\x00\t\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00\x17:\vr" + + "et·4\x00\x00\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00" + + "\t\x13assertE2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11asser" + + "tE2T\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b" + + "\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00\x17" + + ":\x80\x01\x00\x00\x00\t\x13assertI2E2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t" + + "\x11assertI2I\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertI2I" + + "2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"||\x00" + + "\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01" + + "\x00\x00\x01\x00\x00\t\x17panicdottype\x00\x06\x17\"\rhave·1\x00\x00\x17\"\rwant" + + "·2\x00\x00\x17\"\x84\x01\x00\x00\x00\t\rifaceeq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00" + + "\x00\x02\x00l\x00\x00\t\refaceeq\x00\x04:\xa4\x01\x00\x00:\xa6\x01\x00\x00\x02\x00l\x00\x00\t\rmakema" + + "p\x00\b\x17\"\x13mapType·2\x00\x00\n\rhint·3\x00\x00\x17:\x11mapbuf·" + + "4\x00\x00\x17:\x17bucketbuf·5\x00\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapa" + + "ccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rhmap·3\x00\x00\x17:\vkey·4\x00\x00\x02\x17" + + ":\vval·1\x00\x00\t!mapaccess1_fast32\x00\x06\x17\"\xac\x01\x00\x00\x1d::" + + "\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t!mapaccess1_fast64\x00\x06\x17\"\xac" + + "\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t#mapaccess1_fasts" + + "tr\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t\x1bmapaccess" + + "1_fat\x00\b\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00\x17:\xba\x01\x00\x00\x17\"\rzero·5\x00\x00\x02\x17" + + ":\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapType·3\x00\x00\x1d::\rhm" + + "ap·4\x00\x00\x17:\vkey·5\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\rpres·2\x00\x00\t!ma" + + "paccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01" + + "\x00\x00\x00\xd0\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00" + + "\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t#mapaccess2_faststr\x00\x06" + + "\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x1bmapacces" + + "s2_fat\x00\b\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00\x17:\xce\x01\x00\x00\x17\"\rzero·6\x00\x00\x04" + + "\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapType·1\x00\x00" + + "\x1d::\rhmap·2\x00\x00\x17:\vkey·3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15ma" + + "piterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00\t\x11" + + "mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00\x17:\xe2\x01\x00\x00\x00\t\x15mapiter" + + "next\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15chanT" + + "ype·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv1\x00" + + "\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00\x17:p\x00\x00\x00\t\x11c" + + "hanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00\x17:\relem·4" + + "\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf8\x01\x00\x00\x1f\x04:\xfa\x01\x00\x00\x17:p\x00\x00\x00\t\x11c" + + "losechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renable" + + "d\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04" + + "\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"" + + "||\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc·3\x00\x00\x00\t\x1btypedslicec" + + "opy\x00\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selec" + + "tnbsend\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x01\x00\x00\t\x17selectn" + + "brecv\x00\x06\x17\"\xf2\x01\x00\x00\x17:p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19sel" + + "ectnbrecv2\x00\b\x17\"\xf2\x01\x00\x00\x17:p\x00\x00\x17\x00\x15received·4\x00\x00\x1f" + + "\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00" + + "\n\x13selsize·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06" + + "\x17\"\vsel·2\x00\x00\x1f\x04:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00" + + "\t\x13selectrecv\x00\x06\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t" + + "\x15selectrecv2\x00\b\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x17\x00\x15rece" + + "ived·5\x00\x00\x02\x00\xb8\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb6\x02\x00\x00\x02\x00" + + "\xb8\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xae\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makes" + + "lice\x00\x06\x17\"\b\x00\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·" + + "1\x00\x00\t\x11growslice\x00\x06\x17\"\b\x00\x00\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:" + + "\xcc\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00\x17:\vfrm·2\x00\x00\x16\x11le" + + "ngth·3\x00d\x00\t\vmemclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length\xc2" + + "\xb72\x00d\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00\x00\x17:\ay·3\x00\x00\x16\rsiz" + + "e·4\x00d\x01\x00\x00\t\x11memequal8\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13m" + + "emequal16\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04" + + "\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal64\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00" + + "\x00\x01\x00\x00\t\x15memequal128\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x0fint6" + + "4div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64" + + "mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat6" + + "4toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64touint64\x00\x01\x1a\x00\x01\x14\x00\t" + + "\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00" + + "\x01\x14\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e\vnum·2\x00\x00\x1e\vden·" + + "3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefuncenter\x00\x01\x16d\x00\t\x17race" + + "funcexit\x00\x00\x00\t\x0fraceread\x00\x01\x16d\x00\t\x11racewrite\x00\x01\x16" + + "d\x00\t\x19racereadrange\x00\x04\x16\raddr·1\x00d\x16\rsize·2\x00" + + "d\x00\t\x1bracewriterange\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x0fmsanrea" + + "d\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x11msanwrite\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\v\xf4" + + "\x01\x02\v\x00\x01\x00\n$$\n" const unsafeimport = "" + - "c\x00\x03v0\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01:" + - "\x00\x01\x16\x00\t\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\b\x00\v\x00" + - "\x01\x00\n$$\n" + "cn\x00\x03v0\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01" + + ":\x00\x01\x16\x00\t\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\b\x00\v" + + "\x00\x01\x00\n$$\n" diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go index fd57fbd4a7958b..dbefcc7a0bb52d 100644 --- a/src/cmd/compile/internal/gc/cgen.go +++ b/src/cmd/compile/internal/gc/cgen.go @@ -518,8 +518,7 @@ func cgen_wb(n, res *Node, wb bool) { case ODOT, ODOTPTR, OINDEX, - OIND, - ONAME: // PHEAP or PPARAMREF var + OIND: var n1 Node Igen(n, &n1, res) @@ -1545,6 +1544,7 @@ func Agen(n *Node, res *Node) { switch n.Op { default: + Dump("bad agen", n) Fatalf("agen: unknown op %v", Nconv(n, FmtShort|FmtSign)) case OCALLMETH: @@ -1571,24 +1571,6 @@ func Agen(n *Node, res *Node) { Thearch.Gmove(&n1, res) Regfree(&n1) - case ONAME: - // should only get here with names in this func. - if n.Name.Funcdepth > 0 && n.Name.Funcdepth != Funcdepth { - Dump("bad agen", n) - Fatalf("agen: bad ONAME funcdepth %d != %d", n.Name.Funcdepth, Funcdepth) - } - - // should only get here for heap vars or paramref - if n.Class&PHEAP == 0 && n.Class != PPARAMREF { - Dump("bad agen", n) - Fatalf("agen: bad ONAME class %#x", n.Class) - } - - Cgen(n.Name.Heapaddr, res) - if n.Xoffset != 0 { - addOffset(res, n.Xoffset) - } - case OIND: Cgen(nl, res) if !nl.NonNil { @@ -1646,8 +1628,9 @@ func Igen(n *Node, a *Node, res *Node) { switch n.Op { case ONAME: - if (n.Class&PHEAP != 0) || n.Class == PPARAMREF { - break + if n.Class == PAUTOHEAP { + Dump("igen", n) + Fatalf("bad name") } *a = *n return @@ -1702,11 +1685,11 @@ func Igen(n *Node, a *Node, res *Node) { a.Type = n.Type return - // Index of fixed-size array by constant can - // put the offset in the addressing. - // Could do the same for slice except that we need - // to use the real index for the bounds checking. case OINDEX: + // Index of fixed-size array by constant can + // put the offset in the addressing. + // Could do the same for slice except that we need + // to use the real index for the bounds checking. if n.Left.Type.IsArray() || (n.Left.Type.IsPtr() && n.Left.Left.Type.IsArray()) { if Isconst(n.Right, CTINT) { // Compute &a. diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 04fa2509858518..ecdf19a2c4293a 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -66,8 +66,39 @@ func closurebody(body []*Node) *Node { // unhook them. // make the list of pointers for the closure call. for _, v := range func_.Func.Cvars.Slice() { - v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer - v.Name.Param.Outerexpr = oldname(v.Sym) + // Unlink from v1; see comment in syntax.go type Param for these fields. + v1 := v.Name.Defn + v1.Name.Param.Innermost = v.Name.Param.Outer + + // If the closure usage of v is not dense, + // we need to make it dense; now that we're out + // of the function in which v appeared, + // look up v.Sym in the enclosing function + // and keep it around for use in the compiled code. + // + // That is, suppose we just finished parsing the innermost + // closure f4 in this code: + // + // func f() { + // v := 1 + // func() { // f2 + // use(v) + // func() { // f3 + // func() { // f4 + // use(v) + // }() + // }() + // }() + // } + // + // At this point v.Outer is f2's v; there is no f3's v. + // To construct the closure f4 from within f3, + // we need to use f3's v and in this case we need to create f3's v. + // We are now in the context of f3, so calling oldname(v.Sym) + // obtains f3's v, creating it if necessary (as it is in the example). + // + // capturevars will decide whether to use v directly or &v. + v.Name.Param.Outer = oldname(v.Sym) } return func_ @@ -75,7 +106,7 @@ func closurebody(body []*Node) *Node { func typecheckclosure(func_ *Node, top int) { for _, ln := range func_.Func.Cvars.Slice() { - n := ln.Name.Param.Closure + n := ln.Name.Defn if !n.Name.Captured { n.Name.Captured = true if n.Name.Decldepth == 0 { @@ -215,8 +246,6 @@ func makeclosure(func_ *Node) *Node { // We use value capturing for values <= 128 bytes that are never reassigned // after capturing (effectively constant). func capturevars(xfunc *Node) { - var outer *Node - lno := lineno lineno = xfunc.Lineno @@ -239,14 +268,14 @@ func capturevars(xfunc *Node) { // so that the outer frame also grabs them and knows they escape. dowidth(v.Type) - outer = v.Name.Param.Outerexpr - v.Name.Param.Outerexpr = nil + outer := v.Name.Param.Outer + outermost := v.Name.Defn // out parameters will be assigned to implicitly upon return. - if outer.Class != PPARAMOUT && !v.Name.Param.Closure.Addrtaken && !v.Name.Param.Closure.Assigned && v.Type.Width <= 128 { + if outer.Class != PPARAMOUT && !outermost.Addrtaken && !outermost.Assigned && v.Type.Width <= 128 { v.Name.Byval = true } else { - v.Name.Param.Closure.Addrtaken = true + outermost.Addrtaken = true outer = Nod(OADDR, outer, nil) } @@ -259,7 +288,7 @@ func capturevars(xfunc *Node) { if v.Name.Byval { how = "value" } - Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Name.Param.Closure.Addrtaken, v.Name.Param.Closure.Assigned, int32(v.Type.Width)) + Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Addrtaken, outermost.Assigned, int32(v.Type.Width)) } outer = typecheck(outer, Erv) @@ -303,7 +332,7 @@ func transformclosure(xfunc *Node) { continue } fld := newField() - fld.Funarg = true + fld.Funarg = FunargParams if v.Name.Byval { // If v is captured by value, we merely downgrade it to PPARAM. v.Class = PPARAM @@ -313,7 +342,7 @@ func transformclosure(xfunc *Node) { } else { // If v of type T is captured by reference, // we introduce function param &v *T - // and v remains PPARAMREF with &v heapaddr + // and v remains PAUTOHEAP with &v heapaddr // (accesses will implicitly deref &v). addr := newname(Lookupf("&%s", v.Sym.Name)) addr.Type = Ptrto(v.Type) diff --git a/src/cmd/compile/internal/gc/constFold_test.go b/src/cmd/compile/internal/gc/constFold_test.go new file mode 100644 index 00000000000000..118183dd2fa68e --- /dev/null +++ b/src/cmd/compile/internal/gc/constFold_test.go @@ -0,0 +1,12416 @@ +package gc + +import "testing" + +func TestConstFolduint64add(t *testing.T) { + var x, y, r uint64 + x = 0 + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 4294967296 + r = x + y + if r != 4294967296 { + t.Errorf("0 + 4294967296 = %d, want 4294967296", r) + } + y = 18446744073709551615 + r = x + y + if r != 18446744073709551615 { + t.Errorf("0 + 18446744073709551615 = %d, want 18446744073709551615", r) + } + x = 1 + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 4294967296 + r = x + y + if r != 4294967297 { + t.Errorf("1 + 4294967296 = %d, want 4294967297", r) + } + y = 18446744073709551615 + r = x + y + if r != 0 { + t.Errorf("1 + 18446744073709551615 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x + y + if r != 4294967296 { + t.Errorf("4294967296 + 0 = %d, want 4294967296", r) + } + y = 1 + r = x + y + if r != 4294967297 { + t.Errorf("4294967296 + 1 = %d, want 4294967297", r) + } + y = 4294967296 + r = x + y + if r != 8589934592 { + t.Errorf("4294967296 + 4294967296 = %d, want 8589934592", r) + } + y = 18446744073709551615 + r = x + y + if r != 4294967295 { + t.Errorf("4294967296 + 18446744073709551615 = %d, want 4294967295", r) + } + x = 18446744073709551615 + y = 0 + r = x + y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 + 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("18446744073709551615 + 1 = %d, want 0", r) + } + y = 4294967296 + r = x + y + if r != 4294967295 { + t.Errorf("18446744073709551615 + 4294967296 = %d, want 4294967295", r) + } + y = 18446744073709551615 + r = x + y + if r != 18446744073709551614 { + t.Errorf("18446744073709551615 + 18446744073709551615 = %d, want 18446744073709551614", r) + } +} +func TestConstFolduint64sub(t *testing.T) { + var x, y, r uint64 + x = 0 + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != 18446744073709551615 { + t.Errorf("0 - 1 = %d, want 18446744073709551615", r) + } + y = 4294967296 + r = x - y + if r != 18446744069414584320 { + t.Errorf("0 - 4294967296 = %d, want 18446744069414584320", r) + } + y = 18446744073709551615 + r = x - y + if r != 1 { + t.Errorf("0 - 18446744073709551615 = %d, want 1", r) + } + x = 1 + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 4294967296 + r = x - y + if r != 18446744069414584321 { + t.Errorf("1 - 4294967296 = %d, want 18446744069414584321", r) + } + y = 18446744073709551615 + r = x - y + if r != 2 { + t.Errorf("1 - 18446744073709551615 = %d, want 2", r) + } + x = 4294967296 + y = 0 + r = x - y + if r != 4294967296 { + t.Errorf("4294967296 - 0 = %d, want 4294967296", r) + } + y = 1 + r = x - y + if r != 4294967295 { + t.Errorf("4294967296 - 1 = %d, want 4294967295", r) + } + y = 4294967296 + r = x - y + if r != 0 { + t.Errorf("4294967296 - 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x - y + if r != 4294967297 { + t.Errorf("4294967296 - 18446744073709551615 = %d, want 4294967297", r) + } + x = 18446744073709551615 + y = 0 + r = x - y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 - 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x - y + if r != 18446744073709551614 { + t.Errorf("18446744073709551615 - 1 = %d, want 18446744073709551614", r) + } + y = 4294967296 + r = x - y + if r != 18446744069414584319 { + t.Errorf("18446744073709551615 - 4294967296 = %d, want 18446744069414584319", r) + } + y = 18446744073709551615 + r = x - y + if r != 0 { + t.Errorf("18446744073709551615 - 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint64div(t *testing.T) { + var x, y, r uint64 + x = 0 + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 4294967296 + r = x / y + if r != 0 { + t.Errorf("0 / 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x / y + if r != 0 { + t.Errorf("0 / 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 4294967296 + r = x / y + if r != 0 { + t.Errorf("1 / 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x / y + if r != 0 { + t.Errorf("1 / 18446744073709551615 = %d, want 0", r) + } + x = 4294967296 + y = 1 + r = x / y + if r != 4294967296 { + t.Errorf("4294967296 / 1 = %d, want 4294967296", r) + } + y = 4294967296 + r = x / y + if r != 1 { + t.Errorf("4294967296 / 4294967296 = %d, want 1", r) + } + y = 18446744073709551615 + r = x / y + if r != 0 { + t.Errorf("4294967296 / 18446744073709551615 = %d, want 0", r) + } + x = 18446744073709551615 + y = 1 + r = x / y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 / 1 = %d, want 18446744073709551615", r) + } + y = 4294967296 + r = x / y + if r != 4294967295 { + t.Errorf("18446744073709551615 / 4294967296 = %d, want 4294967295", r) + } + y = 18446744073709551615 + r = x / y + if r != 1 { + t.Errorf("18446744073709551615 / 18446744073709551615 = %d, want 1", r) + } +} +func TestConstFolduint64mul(t *testing.T) { + var x, y, r uint64 + x = 0 + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 4294967296 + r = x * y + if r != 0 { + t.Errorf("0 * 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x * y + if r != 0 { + t.Errorf("0 * 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 4294967296 + r = x * y + if r != 4294967296 { + t.Errorf("1 * 4294967296 = %d, want 4294967296", r) + } + y = 18446744073709551615 + r = x * y + if r != 18446744073709551615 { + t.Errorf("1 * 18446744073709551615 = %d, want 18446744073709551615", r) + } + x = 4294967296 + y = 0 + r = x * y + if r != 0 { + t.Errorf("4294967296 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 4294967296 { + t.Errorf("4294967296 * 1 = %d, want 4294967296", r) + } + y = 4294967296 + r = x * y + if r != 0 { + t.Errorf("4294967296 * 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x * y + if r != 18446744069414584320 { + t.Errorf("4294967296 * 18446744073709551615 = %d, want 18446744069414584320", r) + } + x = 18446744073709551615 + y = 0 + r = x * y + if r != 0 { + t.Errorf("18446744073709551615 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 * 1 = %d, want 18446744073709551615", r) + } + y = 4294967296 + r = x * y + if r != 18446744069414584320 { + t.Errorf("18446744073709551615 * 4294967296 = %d, want 18446744069414584320", r) + } + y = 18446744073709551615 + r = x * y + if r != 1 { + t.Errorf("18446744073709551615 * 18446744073709551615 = %d, want 1", r) + } +} +func TestConstFolduint64mod(t *testing.T) { + var x, y, r uint64 + x = 0 + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 0 { + t.Errorf("0 % 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x % y + if r != 0 { + t.Errorf("0 % 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 1 { + t.Errorf("1 % 4294967296 = %d, want 1", r) + } + y = 18446744073709551615 + r = x % y + if r != 1 { + t.Errorf("1 % 18446744073709551615 = %d, want 1", r) + } + x = 4294967296 + y = 1 + r = x % y + if r != 0 { + t.Errorf("4294967296 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 0 { + t.Errorf("4294967296 % 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x % y + if r != 4294967296 { + t.Errorf("4294967296 % 18446744073709551615 = %d, want 4294967296", r) + } + x = 18446744073709551615 + y = 1 + r = x % y + if r != 0 { + t.Errorf("18446744073709551615 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 4294967295 { + t.Errorf("18446744073709551615 % 4294967296 = %d, want 4294967295", r) + } + y = 18446744073709551615 + r = x % y + if r != 0 { + t.Errorf("18446744073709551615 % 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint64add(t *testing.T) { + var x, y, r int64 + x = -9223372036854775808 + y = -9223372036854775808 + r = x + y + if r != 0 { + t.Errorf("-9223372036854775808 + -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x + y + if r != 1 { + t.Errorf("-9223372036854775808 + -9223372036854775807 = %d, want 1", r) + } + y = -4294967296 + r = x + y + if r != 9223372032559808512 { + t.Errorf("-9223372036854775808 + -4294967296 = %d, want 9223372032559808512", r) + } + y = -1 + r = x + y + if r != 9223372036854775807 { + t.Errorf("-9223372036854775808 + -1 = %d, want 9223372036854775807", r) + } + y = 0 + r = x + y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 + 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x + y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775808 + 1 = %d, want -9223372036854775807", r) + } + y = 4294967296 + r = x + y + if r != -9223372032559808512 { + t.Errorf("-9223372036854775808 + 4294967296 = %d, want -9223372032559808512", r) + } + y = 9223372036854775806 + r = x + y + if r != -2 { + t.Errorf("-9223372036854775808 + 9223372036854775806 = %d, want -2", r) + } + y = 9223372036854775807 + r = x + y + if r != -1 { + t.Errorf("-9223372036854775808 + 9223372036854775807 = %d, want -1", r) + } + x = -9223372036854775807 + y = -9223372036854775808 + r = x + y + if r != 1 { + t.Errorf("-9223372036854775807 + -9223372036854775808 = %d, want 1", r) + } + y = -9223372036854775807 + r = x + y + if r != 2 { + t.Errorf("-9223372036854775807 + -9223372036854775807 = %d, want 2", r) + } + y = -4294967296 + r = x + y + if r != 9223372032559808513 { + t.Errorf("-9223372036854775807 + -4294967296 = %d, want 9223372032559808513", r) + } + y = -1 + r = x + y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775807 + -1 = %d, want -9223372036854775808", r) + } + y = 0 + r = x + y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 + 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x + y + if r != -9223372036854775806 { + t.Errorf("-9223372036854775807 + 1 = %d, want -9223372036854775806", r) + } + y = 4294967296 + r = x + y + if r != -9223372032559808511 { + t.Errorf("-9223372036854775807 + 4294967296 = %d, want -9223372032559808511", r) + } + y = 9223372036854775806 + r = x + y + if r != -1 { + t.Errorf("-9223372036854775807 + 9223372036854775806 = %d, want -1", r) + } + y = 9223372036854775807 + r = x + y + if r != 0 { + t.Errorf("-9223372036854775807 + 9223372036854775807 = %d, want 0", r) + } + x = -4294967296 + y = -9223372036854775808 + r = x + y + if r != 9223372032559808512 { + t.Errorf("-4294967296 + -9223372036854775808 = %d, want 9223372032559808512", r) + } + y = -9223372036854775807 + r = x + y + if r != 9223372032559808513 { + t.Errorf("-4294967296 + -9223372036854775807 = %d, want 9223372032559808513", r) + } + y = -4294967296 + r = x + y + if r != -8589934592 { + t.Errorf("-4294967296 + -4294967296 = %d, want -8589934592", r) + } + y = -1 + r = x + y + if r != -4294967297 { + t.Errorf("-4294967296 + -1 = %d, want -4294967297", r) + } + y = 0 + r = x + y + if r != -4294967296 { + t.Errorf("-4294967296 + 0 = %d, want -4294967296", r) + } + y = 1 + r = x + y + if r != -4294967295 { + t.Errorf("-4294967296 + 1 = %d, want -4294967295", r) + } + y = 4294967296 + r = x + y + if r != 0 { + t.Errorf("-4294967296 + 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x + y + if r != 9223372032559808510 { + t.Errorf("-4294967296 + 9223372036854775806 = %d, want 9223372032559808510", r) + } + y = 9223372036854775807 + r = x + y + if r != 9223372032559808511 { + t.Errorf("-4294967296 + 9223372036854775807 = %d, want 9223372032559808511", r) + } + x = -1 + y = -9223372036854775808 + r = x + y + if r != 9223372036854775807 { + t.Errorf("-1 + -9223372036854775808 = %d, want 9223372036854775807", r) + } + y = -9223372036854775807 + r = x + y + if r != -9223372036854775808 { + t.Errorf("-1 + -9223372036854775807 = %d, want -9223372036854775808", r) + } + y = -4294967296 + r = x + y + if r != -4294967297 { + t.Errorf("-1 + -4294967296 = %d, want -4294967297", r) + } + y = -1 + r = x + y + if r != -2 { + t.Errorf("-1 + -1 = %d, want -2", r) + } + y = 0 + r = x + y + if r != -1 { + t.Errorf("-1 + 0 = %d, want -1", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("-1 + 1 = %d, want 0", r) + } + y = 4294967296 + r = x + y + if r != 4294967295 { + t.Errorf("-1 + 4294967296 = %d, want 4294967295", r) + } + y = 9223372036854775806 + r = x + y + if r != 9223372036854775805 { + t.Errorf("-1 + 9223372036854775806 = %d, want 9223372036854775805", r) + } + y = 9223372036854775807 + r = x + y + if r != 9223372036854775806 { + t.Errorf("-1 + 9223372036854775807 = %d, want 9223372036854775806", r) + } + x = 0 + y = -9223372036854775808 + r = x + y + if r != -9223372036854775808 { + t.Errorf("0 + -9223372036854775808 = %d, want -9223372036854775808", r) + } + y = -9223372036854775807 + r = x + y + if r != -9223372036854775807 { + t.Errorf("0 + -9223372036854775807 = %d, want -9223372036854775807", r) + } + y = -4294967296 + r = x + y + if r != -4294967296 { + t.Errorf("0 + -4294967296 = %d, want -4294967296", r) + } + y = -1 + r = x + y + if r != -1 { + t.Errorf("0 + -1 = %d, want -1", r) + } + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 4294967296 + r = x + y + if r != 4294967296 { + t.Errorf("0 + 4294967296 = %d, want 4294967296", r) + } + y = 9223372036854775806 + r = x + y + if r != 9223372036854775806 { + t.Errorf("0 + 9223372036854775806 = %d, want 9223372036854775806", r) + } + y = 9223372036854775807 + r = x + y + if r != 9223372036854775807 { + t.Errorf("0 + 9223372036854775807 = %d, want 9223372036854775807", r) + } + x = 1 + y = -9223372036854775808 + r = x + y + if r != -9223372036854775807 { + t.Errorf("1 + -9223372036854775808 = %d, want -9223372036854775807", r) + } + y = -9223372036854775807 + r = x + y + if r != -9223372036854775806 { + t.Errorf("1 + -9223372036854775807 = %d, want -9223372036854775806", r) + } + y = -4294967296 + r = x + y + if r != -4294967295 { + t.Errorf("1 + -4294967296 = %d, want -4294967295", r) + } + y = -1 + r = x + y + if r != 0 { + t.Errorf("1 + -1 = %d, want 0", r) + } + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 4294967296 + r = x + y + if r != 4294967297 { + t.Errorf("1 + 4294967296 = %d, want 4294967297", r) + } + y = 9223372036854775806 + r = x + y + if r != 9223372036854775807 { + t.Errorf("1 + 9223372036854775806 = %d, want 9223372036854775807", r) + } + y = 9223372036854775807 + r = x + y + if r != -9223372036854775808 { + t.Errorf("1 + 9223372036854775807 = %d, want -9223372036854775808", r) + } + x = 4294967296 + y = -9223372036854775808 + r = x + y + if r != -9223372032559808512 { + t.Errorf("4294967296 + -9223372036854775808 = %d, want -9223372032559808512", r) + } + y = -9223372036854775807 + r = x + y + if r != -9223372032559808511 { + t.Errorf("4294967296 + -9223372036854775807 = %d, want -9223372032559808511", r) + } + y = -4294967296 + r = x + y + if r != 0 { + t.Errorf("4294967296 + -4294967296 = %d, want 0", r) + } + y = -1 + r = x + y + if r != 4294967295 { + t.Errorf("4294967296 + -1 = %d, want 4294967295", r) + } + y = 0 + r = x + y + if r != 4294967296 { + t.Errorf("4294967296 + 0 = %d, want 4294967296", r) + } + y = 1 + r = x + y + if r != 4294967297 { + t.Errorf("4294967296 + 1 = %d, want 4294967297", r) + } + y = 4294967296 + r = x + y + if r != 8589934592 { + t.Errorf("4294967296 + 4294967296 = %d, want 8589934592", r) + } + y = 9223372036854775806 + r = x + y + if r != -9223372032559808514 { + t.Errorf("4294967296 + 9223372036854775806 = %d, want -9223372032559808514", r) + } + y = 9223372036854775807 + r = x + y + if r != -9223372032559808513 { + t.Errorf("4294967296 + 9223372036854775807 = %d, want -9223372032559808513", r) + } + x = 9223372036854775806 + y = -9223372036854775808 + r = x + y + if r != -2 { + t.Errorf("9223372036854775806 + -9223372036854775808 = %d, want -2", r) + } + y = -9223372036854775807 + r = x + y + if r != -1 { + t.Errorf("9223372036854775806 + -9223372036854775807 = %d, want -1", r) + } + y = -4294967296 + r = x + y + if r != 9223372032559808510 { + t.Errorf("9223372036854775806 + -4294967296 = %d, want 9223372032559808510", r) + } + y = -1 + r = x + y + if r != 9223372036854775805 { + t.Errorf("9223372036854775806 + -1 = %d, want 9223372036854775805", r) + } + y = 0 + r = x + y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 + 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x + y + if r != 9223372036854775807 { + t.Errorf("9223372036854775806 + 1 = %d, want 9223372036854775807", r) + } + y = 4294967296 + r = x + y + if r != -9223372032559808514 { + t.Errorf("9223372036854775806 + 4294967296 = %d, want -9223372032559808514", r) + } + y = 9223372036854775806 + r = x + y + if r != -4 { + t.Errorf("9223372036854775806 + 9223372036854775806 = %d, want -4", r) + } + y = 9223372036854775807 + r = x + y + if r != -3 { + t.Errorf("9223372036854775806 + 9223372036854775807 = %d, want -3", r) + } + x = 9223372036854775807 + y = -9223372036854775808 + r = x + y + if r != -1 { + t.Errorf("9223372036854775807 + -9223372036854775808 = %d, want -1", r) + } + y = -9223372036854775807 + r = x + y + if r != 0 { + t.Errorf("9223372036854775807 + -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x + y + if r != 9223372032559808511 { + t.Errorf("9223372036854775807 + -4294967296 = %d, want 9223372032559808511", r) + } + y = -1 + r = x + y + if r != 9223372036854775806 { + t.Errorf("9223372036854775807 + -1 = %d, want 9223372036854775806", r) + } + y = 0 + r = x + y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 + 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x + y + if r != -9223372036854775808 { + t.Errorf("9223372036854775807 + 1 = %d, want -9223372036854775808", r) + } + y = 4294967296 + r = x + y + if r != -9223372032559808513 { + t.Errorf("9223372036854775807 + 4294967296 = %d, want -9223372032559808513", r) + } + y = 9223372036854775806 + r = x + y + if r != -3 { + t.Errorf("9223372036854775807 + 9223372036854775806 = %d, want -3", r) + } + y = 9223372036854775807 + r = x + y + if r != -2 { + t.Errorf("9223372036854775807 + 9223372036854775807 = %d, want -2", r) + } +} +func TestConstFoldint64sub(t *testing.T) { + var x, y, r int64 + x = -9223372036854775808 + y = -9223372036854775808 + r = x - y + if r != 0 { + t.Errorf("-9223372036854775808 - -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x - y + if r != -1 { + t.Errorf("-9223372036854775808 - -9223372036854775807 = %d, want -1", r) + } + y = -4294967296 + r = x - y + if r != -9223372032559808512 { + t.Errorf("-9223372036854775808 - -4294967296 = %d, want -9223372032559808512", r) + } + y = -1 + r = x - y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775808 - -1 = %d, want -9223372036854775807", r) + } + y = 0 + r = x - y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 - 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x - y + if r != 9223372036854775807 { + t.Errorf("-9223372036854775808 - 1 = %d, want 9223372036854775807", r) + } + y = 4294967296 + r = x - y + if r != 9223372032559808512 { + t.Errorf("-9223372036854775808 - 4294967296 = %d, want 9223372032559808512", r) + } + y = 9223372036854775806 + r = x - y + if r != 2 { + t.Errorf("-9223372036854775808 - 9223372036854775806 = %d, want 2", r) + } + y = 9223372036854775807 + r = x - y + if r != 1 { + t.Errorf("-9223372036854775808 - 9223372036854775807 = %d, want 1", r) + } + x = -9223372036854775807 + y = -9223372036854775808 + r = x - y + if r != 1 { + t.Errorf("-9223372036854775807 - -9223372036854775808 = %d, want 1", r) + } + y = -9223372036854775807 + r = x - y + if r != 0 { + t.Errorf("-9223372036854775807 - -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x - y + if r != -9223372032559808511 { + t.Errorf("-9223372036854775807 - -4294967296 = %d, want -9223372032559808511", r) + } + y = -1 + r = x - y + if r != -9223372036854775806 { + t.Errorf("-9223372036854775807 - -1 = %d, want -9223372036854775806", r) + } + y = 0 + r = x - y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 - 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x - y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775807 - 1 = %d, want -9223372036854775808", r) + } + y = 4294967296 + r = x - y + if r != 9223372032559808513 { + t.Errorf("-9223372036854775807 - 4294967296 = %d, want 9223372032559808513", r) + } + y = 9223372036854775806 + r = x - y + if r != 3 { + t.Errorf("-9223372036854775807 - 9223372036854775806 = %d, want 3", r) + } + y = 9223372036854775807 + r = x - y + if r != 2 { + t.Errorf("-9223372036854775807 - 9223372036854775807 = %d, want 2", r) + } + x = -4294967296 + y = -9223372036854775808 + r = x - y + if r != 9223372032559808512 { + t.Errorf("-4294967296 - -9223372036854775808 = %d, want 9223372032559808512", r) + } + y = -9223372036854775807 + r = x - y + if r != 9223372032559808511 { + t.Errorf("-4294967296 - -9223372036854775807 = %d, want 9223372032559808511", r) + } + y = -4294967296 + r = x - y + if r != 0 { + t.Errorf("-4294967296 - -4294967296 = %d, want 0", r) + } + y = -1 + r = x - y + if r != -4294967295 { + t.Errorf("-4294967296 - -1 = %d, want -4294967295", r) + } + y = 0 + r = x - y + if r != -4294967296 { + t.Errorf("-4294967296 - 0 = %d, want -4294967296", r) + } + y = 1 + r = x - y + if r != -4294967297 { + t.Errorf("-4294967296 - 1 = %d, want -4294967297", r) + } + y = 4294967296 + r = x - y + if r != -8589934592 { + t.Errorf("-4294967296 - 4294967296 = %d, want -8589934592", r) + } + y = 9223372036854775806 + r = x - y + if r != 9223372032559808514 { + t.Errorf("-4294967296 - 9223372036854775806 = %d, want 9223372032559808514", r) + } + y = 9223372036854775807 + r = x - y + if r != 9223372032559808513 { + t.Errorf("-4294967296 - 9223372036854775807 = %d, want 9223372032559808513", r) + } + x = -1 + y = -9223372036854775808 + r = x - y + if r != 9223372036854775807 { + t.Errorf("-1 - -9223372036854775808 = %d, want 9223372036854775807", r) + } + y = -9223372036854775807 + r = x - y + if r != 9223372036854775806 { + t.Errorf("-1 - -9223372036854775807 = %d, want 9223372036854775806", r) + } + y = -4294967296 + r = x - y + if r != 4294967295 { + t.Errorf("-1 - -4294967296 = %d, want 4294967295", r) + } + y = -1 + r = x - y + if r != 0 { + t.Errorf("-1 - -1 = %d, want 0", r) + } + y = 0 + r = x - y + if r != -1 { + t.Errorf("-1 - 0 = %d, want -1", r) + } + y = 1 + r = x - y + if r != -2 { + t.Errorf("-1 - 1 = %d, want -2", r) + } + y = 4294967296 + r = x - y + if r != -4294967297 { + t.Errorf("-1 - 4294967296 = %d, want -4294967297", r) + } + y = 9223372036854775806 + r = x - y + if r != -9223372036854775807 { + t.Errorf("-1 - 9223372036854775806 = %d, want -9223372036854775807", r) + } + y = 9223372036854775807 + r = x - y + if r != -9223372036854775808 { + t.Errorf("-1 - 9223372036854775807 = %d, want -9223372036854775808", r) + } + x = 0 + y = -9223372036854775808 + r = x - y + if r != -9223372036854775808 { + t.Errorf("0 - -9223372036854775808 = %d, want -9223372036854775808", r) + } + y = -9223372036854775807 + r = x - y + if r != 9223372036854775807 { + t.Errorf("0 - -9223372036854775807 = %d, want 9223372036854775807", r) + } + y = -4294967296 + r = x - y + if r != 4294967296 { + t.Errorf("0 - -4294967296 = %d, want 4294967296", r) + } + y = -1 + r = x - y + if r != 1 { + t.Errorf("0 - -1 = %d, want 1", r) + } + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != -1 { + t.Errorf("0 - 1 = %d, want -1", r) + } + y = 4294967296 + r = x - y + if r != -4294967296 { + t.Errorf("0 - 4294967296 = %d, want -4294967296", r) + } + y = 9223372036854775806 + r = x - y + if r != -9223372036854775806 { + t.Errorf("0 - 9223372036854775806 = %d, want -9223372036854775806", r) + } + y = 9223372036854775807 + r = x - y + if r != -9223372036854775807 { + t.Errorf("0 - 9223372036854775807 = %d, want -9223372036854775807", r) + } + x = 1 + y = -9223372036854775808 + r = x - y + if r != -9223372036854775807 { + t.Errorf("1 - -9223372036854775808 = %d, want -9223372036854775807", r) + } + y = -9223372036854775807 + r = x - y + if r != -9223372036854775808 { + t.Errorf("1 - -9223372036854775807 = %d, want -9223372036854775808", r) + } + y = -4294967296 + r = x - y + if r != 4294967297 { + t.Errorf("1 - -4294967296 = %d, want 4294967297", r) + } + y = -1 + r = x - y + if r != 2 { + t.Errorf("1 - -1 = %d, want 2", r) + } + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 4294967296 + r = x - y + if r != -4294967295 { + t.Errorf("1 - 4294967296 = %d, want -4294967295", r) + } + y = 9223372036854775806 + r = x - y + if r != -9223372036854775805 { + t.Errorf("1 - 9223372036854775806 = %d, want -9223372036854775805", r) + } + y = 9223372036854775807 + r = x - y + if r != -9223372036854775806 { + t.Errorf("1 - 9223372036854775807 = %d, want -9223372036854775806", r) + } + x = 4294967296 + y = -9223372036854775808 + r = x - y + if r != -9223372032559808512 { + t.Errorf("4294967296 - -9223372036854775808 = %d, want -9223372032559808512", r) + } + y = -9223372036854775807 + r = x - y + if r != -9223372032559808513 { + t.Errorf("4294967296 - -9223372036854775807 = %d, want -9223372032559808513", r) + } + y = -4294967296 + r = x - y + if r != 8589934592 { + t.Errorf("4294967296 - -4294967296 = %d, want 8589934592", r) + } + y = -1 + r = x - y + if r != 4294967297 { + t.Errorf("4294967296 - -1 = %d, want 4294967297", r) + } + y = 0 + r = x - y + if r != 4294967296 { + t.Errorf("4294967296 - 0 = %d, want 4294967296", r) + } + y = 1 + r = x - y + if r != 4294967295 { + t.Errorf("4294967296 - 1 = %d, want 4294967295", r) + } + y = 4294967296 + r = x - y + if r != 0 { + t.Errorf("4294967296 - 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x - y + if r != -9223372032559808510 { + t.Errorf("4294967296 - 9223372036854775806 = %d, want -9223372032559808510", r) + } + y = 9223372036854775807 + r = x - y + if r != -9223372032559808511 { + t.Errorf("4294967296 - 9223372036854775807 = %d, want -9223372032559808511", r) + } + x = 9223372036854775806 + y = -9223372036854775808 + r = x - y + if r != -2 { + t.Errorf("9223372036854775806 - -9223372036854775808 = %d, want -2", r) + } + y = -9223372036854775807 + r = x - y + if r != -3 { + t.Errorf("9223372036854775806 - -9223372036854775807 = %d, want -3", r) + } + y = -4294967296 + r = x - y + if r != -9223372032559808514 { + t.Errorf("9223372036854775806 - -4294967296 = %d, want -9223372032559808514", r) + } + y = -1 + r = x - y + if r != 9223372036854775807 { + t.Errorf("9223372036854775806 - -1 = %d, want 9223372036854775807", r) + } + y = 0 + r = x - y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 - 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x - y + if r != 9223372036854775805 { + t.Errorf("9223372036854775806 - 1 = %d, want 9223372036854775805", r) + } + y = 4294967296 + r = x - y + if r != 9223372032559808510 { + t.Errorf("9223372036854775806 - 4294967296 = %d, want 9223372032559808510", r) + } + y = 9223372036854775806 + r = x - y + if r != 0 { + t.Errorf("9223372036854775806 - 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x - y + if r != -1 { + t.Errorf("9223372036854775806 - 9223372036854775807 = %d, want -1", r) + } + x = 9223372036854775807 + y = -9223372036854775808 + r = x - y + if r != -1 { + t.Errorf("9223372036854775807 - -9223372036854775808 = %d, want -1", r) + } + y = -9223372036854775807 + r = x - y + if r != -2 { + t.Errorf("9223372036854775807 - -9223372036854775807 = %d, want -2", r) + } + y = -4294967296 + r = x - y + if r != -9223372032559808513 { + t.Errorf("9223372036854775807 - -4294967296 = %d, want -9223372032559808513", r) + } + y = -1 + r = x - y + if r != -9223372036854775808 { + t.Errorf("9223372036854775807 - -1 = %d, want -9223372036854775808", r) + } + y = 0 + r = x - y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 - 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x - y + if r != 9223372036854775806 { + t.Errorf("9223372036854775807 - 1 = %d, want 9223372036854775806", r) + } + y = 4294967296 + r = x - y + if r != 9223372032559808511 { + t.Errorf("9223372036854775807 - 4294967296 = %d, want 9223372032559808511", r) + } + y = 9223372036854775806 + r = x - y + if r != 1 { + t.Errorf("9223372036854775807 - 9223372036854775806 = %d, want 1", r) + } + y = 9223372036854775807 + r = x - y + if r != 0 { + t.Errorf("9223372036854775807 - 9223372036854775807 = %d, want 0", r) + } +} +func TestConstFoldint64div(t *testing.T) { + var x, y, r int64 + x = -9223372036854775808 + y = -9223372036854775808 + r = x / y + if r != 1 { + t.Errorf("-9223372036854775808 / -9223372036854775808 = %d, want 1", r) + } + y = -9223372036854775807 + r = x / y + if r != 1 { + t.Errorf("-9223372036854775808 / -9223372036854775807 = %d, want 1", r) + } + y = -4294967296 + r = x / y + if r != 2147483648 { + t.Errorf("-9223372036854775808 / -4294967296 = %d, want 2147483648", r) + } + y = -1 + r = x / y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 / -1 = %d, want -9223372036854775808", r) + } + y = 1 + r = x / y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 / 1 = %d, want -9223372036854775808", r) + } + y = 4294967296 + r = x / y + if r != -2147483648 { + t.Errorf("-9223372036854775808 / 4294967296 = %d, want -2147483648", r) + } + y = 9223372036854775806 + r = x / y + if r != -1 { + t.Errorf("-9223372036854775808 / 9223372036854775806 = %d, want -1", r) + } + y = 9223372036854775807 + r = x / y + if r != -1 { + t.Errorf("-9223372036854775808 / 9223372036854775807 = %d, want -1", r) + } + x = -9223372036854775807 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("-9223372036854775807 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 1 { + t.Errorf("-9223372036854775807 / -9223372036854775807 = %d, want 1", r) + } + y = -4294967296 + r = x / y + if r != 2147483647 { + t.Errorf("-9223372036854775807 / -4294967296 = %d, want 2147483647", r) + } + y = -1 + r = x / y + if r != 9223372036854775807 { + t.Errorf("-9223372036854775807 / -1 = %d, want 9223372036854775807", r) + } + y = 1 + r = x / y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 / 1 = %d, want -9223372036854775807", r) + } + y = 4294967296 + r = x / y + if r != -2147483647 { + t.Errorf("-9223372036854775807 / 4294967296 = %d, want -2147483647", r) + } + y = 9223372036854775806 + r = x / y + if r != -1 { + t.Errorf("-9223372036854775807 / 9223372036854775806 = %d, want -1", r) + } + y = 9223372036854775807 + r = x / y + if r != -1 { + t.Errorf("-9223372036854775807 / 9223372036854775807 = %d, want -1", r) + } + x = -4294967296 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("-4294967296 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("-4294967296 / -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x / y + if r != 1 { + t.Errorf("-4294967296 / -4294967296 = %d, want 1", r) + } + y = -1 + r = x / y + if r != 4294967296 { + t.Errorf("-4294967296 / -1 = %d, want 4294967296", r) + } + y = 1 + r = x / y + if r != -4294967296 { + t.Errorf("-4294967296 / 1 = %d, want -4294967296", r) + } + y = 4294967296 + r = x / y + if r != -1 { + t.Errorf("-4294967296 / 4294967296 = %d, want -1", r) + } + y = 9223372036854775806 + r = x / y + if r != 0 { + t.Errorf("-4294967296 / 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("-4294967296 / 9223372036854775807 = %d, want 0", r) + } + x = -1 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("-1 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("-1 / -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x / y + if r != 0 { + t.Errorf("-1 / -4294967296 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 1 { + t.Errorf("-1 / -1 = %d, want 1", r) + } + y = 1 + r = x / y + if r != -1 { + t.Errorf("-1 / 1 = %d, want -1", r) + } + y = 4294967296 + r = x / y + if r != 0 { + t.Errorf("-1 / 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x / y + if r != 0 { + t.Errorf("-1 / 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("-1 / 9223372036854775807 = %d, want 0", r) + } + x = 0 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("0 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("0 / -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x / y + if r != 0 { + t.Errorf("0 / -4294967296 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 0 { + t.Errorf("0 / -1 = %d, want 0", r) + } + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 4294967296 + r = x / y + if r != 0 { + t.Errorf("0 / 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x / y + if r != 0 { + t.Errorf("0 / 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("0 / 9223372036854775807 = %d, want 0", r) + } + x = 1 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("1 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("1 / -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x / y + if r != 0 { + t.Errorf("1 / -4294967296 = %d, want 0", r) + } + y = -1 + r = x / y + if r != -1 { + t.Errorf("1 / -1 = %d, want -1", r) + } + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 4294967296 + r = x / y + if r != 0 { + t.Errorf("1 / 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x / y + if r != 0 { + t.Errorf("1 / 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("1 / 9223372036854775807 = %d, want 0", r) + } + x = 4294967296 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("4294967296 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("4294967296 / -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x / y + if r != -1 { + t.Errorf("4294967296 / -4294967296 = %d, want -1", r) + } + y = -1 + r = x / y + if r != -4294967296 { + t.Errorf("4294967296 / -1 = %d, want -4294967296", r) + } + y = 1 + r = x / y + if r != 4294967296 { + t.Errorf("4294967296 / 1 = %d, want 4294967296", r) + } + y = 4294967296 + r = x / y + if r != 1 { + t.Errorf("4294967296 / 4294967296 = %d, want 1", r) + } + y = 9223372036854775806 + r = x / y + if r != 0 { + t.Errorf("4294967296 / 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("4294967296 / 9223372036854775807 = %d, want 0", r) + } + x = 9223372036854775806 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("9223372036854775806 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("9223372036854775806 / -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x / y + if r != -2147483647 { + t.Errorf("9223372036854775806 / -4294967296 = %d, want -2147483647", r) + } + y = -1 + r = x / y + if r != -9223372036854775806 { + t.Errorf("9223372036854775806 / -1 = %d, want -9223372036854775806", r) + } + y = 1 + r = x / y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 / 1 = %d, want 9223372036854775806", r) + } + y = 4294967296 + r = x / y + if r != 2147483647 { + t.Errorf("9223372036854775806 / 4294967296 = %d, want 2147483647", r) + } + y = 9223372036854775806 + r = x / y + if r != 1 { + t.Errorf("9223372036854775806 / 9223372036854775806 = %d, want 1", r) + } + y = 9223372036854775807 + r = x / y + if r != 0 { + t.Errorf("9223372036854775806 / 9223372036854775807 = %d, want 0", r) + } + x = 9223372036854775807 + y = -9223372036854775808 + r = x / y + if r != 0 { + t.Errorf("9223372036854775807 / -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x / y + if r != -1 { + t.Errorf("9223372036854775807 / -9223372036854775807 = %d, want -1", r) + } + y = -4294967296 + r = x / y + if r != -2147483647 { + t.Errorf("9223372036854775807 / -4294967296 = %d, want -2147483647", r) + } + y = -1 + r = x / y + if r != -9223372036854775807 { + t.Errorf("9223372036854775807 / -1 = %d, want -9223372036854775807", r) + } + y = 1 + r = x / y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 / 1 = %d, want 9223372036854775807", r) + } + y = 4294967296 + r = x / y + if r != 2147483647 { + t.Errorf("9223372036854775807 / 4294967296 = %d, want 2147483647", r) + } + y = 9223372036854775806 + r = x / y + if r != 1 { + t.Errorf("9223372036854775807 / 9223372036854775806 = %d, want 1", r) + } + y = 9223372036854775807 + r = x / y + if r != 1 { + t.Errorf("9223372036854775807 / 9223372036854775807 = %d, want 1", r) + } +} +func TestConstFoldint64mul(t *testing.T) { + var x, y, r int64 + x = -9223372036854775808 + y = -9223372036854775808 + r = x * y + if r != 0 { + t.Errorf("-9223372036854775808 * -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x * y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 * -9223372036854775807 = %d, want -9223372036854775808", r) + } + y = -4294967296 + r = x * y + if r != 0 { + t.Errorf("-9223372036854775808 * -4294967296 = %d, want 0", r) + } + y = -1 + r = x * y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 * -1 = %d, want -9223372036854775808", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-9223372036854775808 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 * 1 = %d, want -9223372036854775808", r) + } + y = 4294967296 + r = x * y + if r != 0 { + t.Errorf("-9223372036854775808 * 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x * y + if r != 0 { + t.Errorf("-9223372036854775808 * 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x * y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 * 9223372036854775807 = %d, want -9223372036854775808", r) + } + x = -9223372036854775807 + y = -9223372036854775808 + r = x * y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775807 * -9223372036854775808 = %d, want -9223372036854775808", r) + } + y = -9223372036854775807 + r = x * y + if r != 1 { + t.Errorf("-9223372036854775807 * -9223372036854775807 = %d, want 1", r) + } + y = -4294967296 + r = x * y + if r != -4294967296 { + t.Errorf("-9223372036854775807 * -4294967296 = %d, want -4294967296", r) + } + y = -1 + r = x * y + if r != 9223372036854775807 { + t.Errorf("-9223372036854775807 * -1 = %d, want 9223372036854775807", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-9223372036854775807 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 * 1 = %d, want -9223372036854775807", r) + } + y = 4294967296 + r = x * y + if r != 4294967296 { + t.Errorf("-9223372036854775807 * 4294967296 = %d, want 4294967296", r) + } + y = 9223372036854775806 + r = x * y + if r != 9223372036854775806 { + t.Errorf("-9223372036854775807 * 9223372036854775806 = %d, want 9223372036854775806", r) + } + y = 9223372036854775807 + r = x * y + if r != -1 { + t.Errorf("-9223372036854775807 * 9223372036854775807 = %d, want -1", r) + } + x = -4294967296 + y = -9223372036854775808 + r = x * y + if r != 0 { + t.Errorf("-4294967296 * -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x * y + if r != -4294967296 { + t.Errorf("-4294967296 * -9223372036854775807 = %d, want -4294967296", r) + } + y = -4294967296 + r = x * y + if r != 0 { + t.Errorf("-4294967296 * -4294967296 = %d, want 0", r) + } + y = -1 + r = x * y + if r != 4294967296 { + t.Errorf("-4294967296 * -1 = %d, want 4294967296", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-4294967296 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -4294967296 { + t.Errorf("-4294967296 * 1 = %d, want -4294967296", r) + } + y = 4294967296 + r = x * y + if r != 0 { + t.Errorf("-4294967296 * 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x * y + if r != 8589934592 { + t.Errorf("-4294967296 * 9223372036854775806 = %d, want 8589934592", r) + } + y = 9223372036854775807 + r = x * y + if r != 4294967296 { + t.Errorf("-4294967296 * 9223372036854775807 = %d, want 4294967296", r) + } + x = -1 + y = -9223372036854775808 + r = x * y + if r != -9223372036854775808 { + t.Errorf("-1 * -9223372036854775808 = %d, want -9223372036854775808", r) + } + y = -9223372036854775807 + r = x * y + if r != 9223372036854775807 { + t.Errorf("-1 * -9223372036854775807 = %d, want 9223372036854775807", r) + } + y = -4294967296 + r = x * y + if r != 4294967296 { + t.Errorf("-1 * -4294967296 = %d, want 4294967296", r) + } + y = -1 + r = x * y + if r != 1 { + t.Errorf("-1 * -1 = %d, want 1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -1 { + t.Errorf("-1 * 1 = %d, want -1", r) + } + y = 4294967296 + r = x * y + if r != -4294967296 { + t.Errorf("-1 * 4294967296 = %d, want -4294967296", r) + } + y = 9223372036854775806 + r = x * y + if r != -9223372036854775806 { + t.Errorf("-1 * 9223372036854775806 = %d, want -9223372036854775806", r) + } + y = 9223372036854775807 + r = x * y + if r != -9223372036854775807 { + t.Errorf("-1 * 9223372036854775807 = %d, want -9223372036854775807", r) + } + x = 0 + y = -9223372036854775808 + r = x * y + if r != 0 { + t.Errorf("0 * -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x * y + if r != 0 { + t.Errorf("0 * -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x * y + if r != 0 { + t.Errorf("0 * -4294967296 = %d, want 0", r) + } + y = -1 + r = x * y + if r != 0 { + t.Errorf("0 * -1 = %d, want 0", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 4294967296 + r = x * y + if r != 0 { + t.Errorf("0 * 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x * y + if r != 0 { + t.Errorf("0 * 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x * y + if r != 0 { + t.Errorf("0 * 9223372036854775807 = %d, want 0", r) + } + x = 1 + y = -9223372036854775808 + r = x * y + if r != -9223372036854775808 { + t.Errorf("1 * -9223372036854775808 = %d, want -9223372036854775808", r) + } + y = -9223372036854775807 + r = x * y + if r != -9223372036854775807 { + t.Errorf("1 * -9223372036854775807 = %d, want -9223372036854775807", r) + } + y = -4294967296 + r = x * y + if r != -4294967296 { + t.Errorf("1 * -4294967296 = %d, want -4294967296", r) + } + y = -1 + r = x * y + if r != -1 { + t.Errorf("1 * -1 = %d, want -1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 4294967296 + r = x * y + if r != 4294967296 { + t.Errorf("1 * 4294967296 = %d, want 4294967296", r) + } + y = 9223372036854775806 + r = x * y + if r != 9223372036854775806 { + t.Errorf("1 * 9223372036854775806 = %d, want 9223372036854775806", r) + } + y = 9223372036854775807 + r = x * y + if r != 9223372036854775807 { + t.Errorf("1 * 9223372036854775807 = %d, want 9223372036854775807", r) + } + x = 4294967296 + y = -9223372036854775808 + r = x * y + if r != 0 { + t.Errorf("4294967296 * -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x * y + if r != 4294967296 { + t.Errorf("4294967296 * -9223372036854775807 = %d, want 4294967296", r) + } + y = -4294967296 + r = x * y + if r != 0 { + t.Errorf("4294967296 * -4294967296 = %d, want 0", r) + } + y = -1 + r = x * y + if r != -4294967296 { + t.Errorf("4294967296 * -1 = %d, want -4294967296", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("4294967296 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 4294967296 { + t.Errorf("4294967296 * 1 = %d, want 4294967296", r) + } + y = 4294967296 + r = x * y + if r != 0 { + t.Errorf("4294967296 * 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x * y + if r != -8589934592 { + t.Errorf("4294967296 * 9223372036854775806 = %d, want -8589934592", r) + } + y = 9223372036854775807 + r = x * y + if r != -4294967296 { + t.Errorf("4294967296 * 9223372036854775807 = %d, want -4294967296", r) + } + x = 9223372036854775806 + y = -9223372036854775808 + r = x * y + if r != 0 { + t.Errorf("9223372036854775806 * -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x * y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 * -9223372036854775807 = %d, want 9223372036854775806", r) + } + y = -4294967296 + r = x * y + if r != 8589934592 { + t.Errorf("9223372036854775806 * -4294967296 = %d, want 8589934592", r) + } + y = -1 + r = x * y + if r != -9223372036854775806 { + t.Errorf("9223372036854775806 * -1 = %d, want -9223372036854775806", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("9223372036854775806 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 * 1 = %d, want 9223372036854775806", r) + } + y = 4294967296 + r = x * y + if r != -8589934592 { + t.Errorf("9223372036854775806 * 4294967296 = %d, want -8589934592", r) + } + y = 9223372036854775806 + r = x * y + if r != 4 { + t.Errorf("9223372036854775806 * 9223372036854775806 = %d, want 4", r) + } + y = 9223372036854775807 + r = x * y + if r != -9223372036854775806 { + t.Errorf("9223372036854775806 * 9223372036854775807 = %d, want -9223372036854775806", r) + } + x = 9223372036854775807 + y = -9223372036854775808 + r = x * y + if r != -9223372036854775808 { + t.Errorf("9223372036854775807 * -9223372036854775808 = %d, want -9223372036854775808", r) + } + y = -9223372036854775807 + r = x * y + if r != -1 { + t.Errorf("9223372036854775807 * -9223372036854775807 = %d, want -1", r) + } + y = -4294967296 + r = x * y + if r != 4294967296 { + t.Errorf("9223372036854775807 * -4294967296 = %d, want 4294967296", r) + } + y = -1 + r = x * y + if r != -9223372036854775807 { + t.Errorf("9223372036854775807 * -1 = %d, want -9223372036854775807", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("9223372036854775807 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 * 1 = %d, want 9223372036854775807", r) + } + y = 4294967296 + r = x * y + if r != -4294967296 { + t.Errorf("9223372036854775807 * 4294967296 = %d, want -4294967296", r) + } + y = 9223372036854775806 + r = x * y + if r != -9223372036854775806 { + t.Errorf("9223372036854775807 * 9223372036854775806 = %d, want -9223372036854775806", r) + } + y = 9223372036854775807 + r = x * y + if r != 1 { + t.Errorf("9223372036854775807 * 9223372036854775807 = %d, want 1", r) + } +} +func TestConstFoldint64mod(t *testing.T) { + var x, y, r int64 + x = -9223372036854775808 + y = -9223372036854775808 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775808 % -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x % y + if r != -1 { + t.Errorf("-9223372036854775808 % -9223372036854775807 = %d, want -1", r) + } + y = -4294967296 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775808 % -4294967296 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775808 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775808 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775808 % 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x % y + if r != -2 { + t.Errorf("-9223372036854775808 % 9223372036854775806 = %d, want -2", r) + } + y = 9223372036854775807 + r = x % y + if r != -1 { + t.Errorf("-9223372036854775808 % 9223372036854775807 = %d, want -1", r) + } + x = -9223372036854775807 + y = -9223372036854775808 + r = x % y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 % -9223372036854775808 = %d, want -9223372036854775807", r) + } + y = -9223372036854775807 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775807 % -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x % y + if r != -4294967295 { + t.Errorf("-9223372036854775807 % -4294967296 = %d, want -4294967295", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775807 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775807 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != -4294967295 { + t.Errorf("-9223372036854775807 % 4294967296 = %d, want -4294967295", r) + } + y = 9223372036854775806 + r = x % y + if r != -1 { + t.Errorf("-9223372036854775807 % 9223372036854775806 = %d, want -1", r) + } + y = 9223372036854775807 + r = x % y + if r != 0 { + t.Errorf("-9223372036854775807 % 9223372036854775807 = %d, want 0", r) + } + x = -4294967296 + y = -9223372036854775808 + r = x % y + if r != -4294967296 { + t.Errorf("-4294967296 % -9223372036854775808 = %d, want -4294967296", r) + } + y = -9223372036854775807 + r = x % y + if r != -4294967296 { + t.Errorf("-4294967296 % -9223372036854775807 = %d, want -4294967296", r) + } + y = -4294967296 + r = x % y + if r != 0 { + t.Errorf("-4294967296 % -4294967296 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-4294967296 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-4294967296 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 0 { + t.Errorf("-4294967296 % 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x % y + if r != -4294967296 { + t.Errorf("-4294967296 % 9223372036854775806 = %d, want -4294967296", r) + } + y = 9223372036854775807 + r = x % y + if r != -4294967296 { + t.Errorf("-4294967296 % 9223372036854775807 = %d, want -4294967296", r) + } + x = -1 + y = -9223372036854775808 + r = x % y + if r != -1 { + t.Errorf("-1 % -9223372036854775808 = %d, want -1", r) + } + y = -9223372036854775807 + r = x % y + if r != -1 { + t.Errorf("-1 % -9223372036854775807 = %d, want -1", r) + } + y = -4294967296 + r = x % y + if r != -1 { + t.Errorf("-1 % -4294967296 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-1 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != -1 { + t.Errorf("-1 % 4294967296 = %d, want -1", r) + } + y = 9223372036854775806 + r = x % y + if r != -1 { + t.Errorf("-1 % 9223372036854775806 = %d, want -1", r) + } + y = 9223372036854775807 + r = x % y + if r != -1 { + t.Errorf("-1 % 9223372036854775807 = %d, want -1", r) + } + x = 0 + y = -9223372036854775808 + r = x % y + if r != 0 { + t.Errorf("0 % -9223372036854775808 = %d, want 0", r) + } + y = -9223372036854775807 + r = x % y + if r != 0 { + t.Errorf("0 % -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x % y + if r != 0 { + t.Errorf("0 % -4294967296 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("0 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 0 { + t.Errorf("0 % 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x % y + if r != 0 { + t.Errorf("0 % 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x % y + if r != 0 { + t.Errorf("0 % 9223372036854775807 = %d, want 0", r) + } + x = 1 + y = -9223372036854775808 + r = x % y + if r != 1 { + t.Errorf("1 % -9223372036854775808 = %d, want 1", r) + } + y = -9223372036854775807 + r = x % y + if r != 1 { + t.Errorf("1 % -9223372036854775807 = %d, want 1", r) + } + y = -4294967296 + r = x % y + if r != 1 { + t.Errorf("1 % -4294967296 = %d, want 1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 1 { + t.Errorf("1 % 4294967296 = %d, want 1", r) + } + y = 9223372036854775806 + r = x % y + if r != 1 { + t.Errorf("1 % 9223372036854775806 = %d, want 1", r) + } + y = 9223372036854775807 + r = x % y + if r != 1 { + t.Errorf("1 % 9223372036854775807 = %d, want 1", r) + } + x = 4294967296 + y = -9223372036854775808 + r = x % y + if r != 4294967296 { + t.Errorf("4294967296 % -9223372036854775808 = %d, want 4294967296", r) + } + y = -9223372036854775807 + r = x % y + if r != 4294967296 { + t.Errorf("4294967296 % -9223372036854775807 = %d, want 4294967296", r) + } + y = -4294967296 + r = x % y + if r != 0 { + t.Errorf("4294967296 % -4294967296 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("4294967296 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("4294967296 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 0 { + t.Errorf("4294967296 % 4294967296 = %d, want 0", r) + } + y = 9223372036854775806 + r = x % y + if r != 4294967296 { + t.Errorf("4294967296 % 9223372036854775806 = %d, want 4294967296", r) + } + y = 9223372036854775807 + r = x % y + if r != 4294967296 { + t.Errorf("4294967296 % 9223372036854775807 = %d, want 4294967296", r) + } + x = 9223372036854775806 + y = -9223372036854775808 + r = x % y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 % -9223372036854775808 = %d, want 9223372036854775806", r) + } + y = -9223372036854775807 + r = x % y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 % -9223372036854775807 = %d, want 9223372036854775806", r) + } + y = -4294967296 + r = x % y + if r != 4294967294 { + t.Errorf("9223372036854775806 % -4294967296 = %d, want 4294967294", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("9223372036854775806 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("9223372036854775806 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 4294967294 { + t.Errorf("9223372036854775806 % 4294967296 = %d, want 4294967294", r) + } + y = 9223372036854775806 + r = x % y + if r != 0 { + t.Errorf("9223372036854775806 % 9223372036854775806 = %d, want 0", r) + } + y = 9223372036854775807 + r = x % y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 % 9223372036854775807 = %d, want 9223372036854775806", r) + } + x = 9223372036854775807 + y = -9223372036854775808 + r = x % y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 % -9223372036854775808 = %d, want 9223372036854775807", r) + } + y = -9223372036854775807 + r = x % y + if r != 0 { + t.Errorf("9223372036854775807 % -9223372036854775807 = %d, want 0", r) + } + y = -4294967296 + r = x % y + if r != 4294967295 { + t.Errorf("9223372036854775807 % -4294967296 = %d, want 4294967295", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("9223372036854775807 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("9223372036854775807 % 1 = %d, want 0", r) + } + y = 4294967296 + r = x % y + if r != 4294967295 { + t.Errorf("9223372036854775807 % 4294967296 = %d, want 4294967295", r) + } + y = 9223372036854775806 + r = x % y + if r != 1 { + t.Errorf("9223372036854775807 % 9223372036854775806 = %d, want 1", r) + } + y = 9223372036854775807 + r = x % y + if r != 0 { + t.Errorf("9223372036854775807 % 9223372036854775807 = %d, want 0", r) + } +} +func TestConstFolduint32add(t *testing.T) { + var x, y, r uint32 + x = 0 + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 4294967295 + r = x + y + if r != 4294967295 { + t.Errorf("0 + 4294967295 = %d, want 4294967295", r) + } + x = 1 + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 4294967295 + r = x + y + if r != 0 { + t.Errorf("1 + 4294967295 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x + y + if r != 4294967295 { + t.Errorf("4294967295 + 0 = %d, want 4294967295", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("4294967295 + 1 = %d, want 0", r) + } + y = 4294967295 + r = x + y + if r != 4294967294 { + t.Errorf("4294967295 + 4294967295 = %d, want 4294967294", r) + } +} +func TestConstFolduint32sub(t *testing.T) { + var x, y, r uint32 + x = 0 + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != 4294967295 { + t.Errorf("0 - 1 = %d, want 4294967295", r) + } + y = 4294967295 + r = x - y + if r != 1 { + t.Errorf("0 - 4294967295 = %d, want 1", r) + } + x = 1 + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 4294967295 + r = x - y + if r != 2 { + t.Errorf("1 - 4294967295 = %d, want 2", r) + } + x = 4294967295 + y = 0 + r = x - y + if r != 4294967295 { + t.Errorf("4294967295 - 0 = %d, want 4294967295", r) + } + y = 1 + r = x - y + if r != 4294967294 { + t.Errorf("4294967295 - 1 = %d, want 4294967294", r) + } + y = 4294967295 + r = x - y + if r != 0 { + t.Errorf("4294967295 - 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint32div(t *testing.T) { + var x, y, r uint32 + x = 0 + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 4294967295 + r = x / y + if r != 0 { + t.Errorf("0 / 4294967295 = %d, want 0", r) + } + x = 1 + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 4294967295 + r = x / y + if r != 0 { + t.Errorf("1 / 4294967295 = %d, want 0", r) + } + x = 4294967295 + y = 1 + r = x / y + if r != 4294967295 { + t.Errorf("4294967295 / 1 = %d, want 4294967295", r) + } + y = 4294967295 + r = x / y + if r != 1 { + t.Errorf("4294967295 / 4294967295 = %d, want 1", r) + } +} +func TestConstFolduint32mul(t *testing.T) { + var x, y, r uint32 + x = 0 + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 4294967295 + r = x * y + if r != 0 { + t.Errorf("0 * 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 4294967295 + r = x * y + if r != 4294967295 { + t.Errorf("1 * 4294967295 = %d, want 4294967295", r) + } + x = 4294967295 + y = 0 + r = x * y + if r != 0 { + t.Errorf("4294967295 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 4294967295 { + t.Errorf("4294967295 * 1 = %d, want 4294967295", r) + } + y = 4294967295 + r = x * y + if r != 1 { + t.Errorf("4294967295 * 4294967295 = %d, want 1", r) + } +} +func TestConstFolduint32mod(t *testing.T) { + var x, y, r uint32 + x = 0 + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 4294967295 + r = x % y + if r != 0 { + t.Errorf("0 % 4294967295 = %d, want 0", r) + } + x = 1 + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 4294967295 + r = x % y + if r != 1 { + t.Errorf("1 % 4294967295 = %d, want 1", r) + } + x = 4294967295 + y = 1 + r = x % y + if r != 0 { + t.Errorf("4294967295 % 1 = %d, want 0", r) + } + y = 4294967295 + r = x % y + if r != 0 { + t.Errorf("4294967295 % 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint32add(t *testing.T) { + var x, y, r int32 + x = -2147483648 + y = -2147483648 + r = x + y + if r != 0 { + t.Errorf("-2147483648 + -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x + y + if r != 1 { + t.Errorf("-2147483648 + -2147483647 = %d, want 1", r) + } + y = -1 + r = x + y + if r != 2147483647 { + t.Errorf("-2147483648 + -1 = %d, want 2147483647", r) + } + y = 0 + r = x + y + if r != -2147483648 { + t.Errorf("-2147483648 + 0 = %d, want -2147483648", r) + } + y = 1 + r = x + y + if r != -2147483647 { + t.Errorf("-2147483648 + 1 = %d, want -2147483647", r) + } + y = 2147483647 + r = x + y + if r != -1 { + t.Errorf("-2147483648 + 2147483647 = %d, want -1", r) + } + x = -2147483647 + y = -2147483648 + r = x + y + if r != 1 { + t.Errorf("-2147483647 + -2147483648 = %d, want 1", r) + } + y = -2147483647 + r = x + y + if r != 2 { + t.Errorf("-2147483647 + -2147483647 = %d, want 2", r) + } + y = -1 + r = x + y + if r != -2147483648 { + t.Errorf("-2147483647 + -1 = %d, want -2147483648", r) + } + y = 0 + r = x + y + if r != -2147483647 { + t.Errorf("-2147483647 + 0 = %d, want -2147483647", r) + } + y = 1 + r = x + y + if r != -2147483646 { + t.Errorf("-2147483647 + 1 = %d, want -2147483646", r) + } + y = 2147483647 + r = x + y + if r != 0 { + t.Errorf("-2147483647 + 2147483647 = %d, want 0", r) + } + x = -1 + y = -2147483648 + r = x + y + if r != 2147483647 { + t.Errorf("-1 + -2147483648 = %d, want 2147483647", r) + } + y = -2147483647 + r = x + y + if r != -2147483648 { + t.Errorf("-1 + -2147483647 = %d, want -2147483648", r) + } + y = -1 + r = x + y + if r != -2 { + t.Errorf("-1 + -1 = %d, want -2", r) + } + y = 0 + r = x + y + if r != -1 { + t.Errorf("-1 + 0 = %d, want -1", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("-1 + 1 = %d, want 0", r) + } + y = 2147483647 + r = x + y + if r != 2147483646 { + t.Errorf("-1 + 2147483647 = %d, want 2147483646", r) + } + x = 0 + y = -2147483648 + r = x + y + if r != -2147483648 { + t.Errorf("0 + -2147483648 = %d, want -2147483648", r) + } + y = -2147483647 + r = x + y + if r != -2147483647 { + t.Errorf("0 + -2147483647 = %d, want -2147483647", r) + } + y = -1 + r = x + y + if r != -1 { + t.Errorf("0 + -1 = %d, want -1", r) + } + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 2147483647 + r = x + y + if r != 2147483647 { + t.Errorf("0 + 2147483647 = %d, want 2147483647", r) + } + x = 1 + y = -2147483648 + r = x + y + if r != -2147483647 { + t.Errorf("1 + -2147483648 = %d, want -2147483647", r) + } + y = -2147483647 + r = x + y + if r != -2147483646 { + t.Errorf("1 + -2147483647 = %d, want -2147483646", r) + } + y = -1 + r = x + y + if r != 0 { + t.Errorf("1 + -1 = %d, want 0", r) + } + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 2147483647 + r = x + y + if r != -2147483648 { + t.Errorf("1 + 2147483647 = %d, want -2147483648", r) + } + x = 2147483647 + y = -2147483648 + r = x + y + if r != -1 { + t.Errorf("2147483647 + -2147483648 = %d, want -1", r) + } + y = -2147483647 + r = x + y + if r != 0 { + t.Errorf("2147483647 + -2147483647 = %d, want 0", r) + } + y = -1 + r = x + y + if r != 2147483646 { + t.Errorf("2147483647 + -1 = %d, want 2147483646", r) + } + y = 0 + r = x + y + if r != 2147483647 { + t.Errorf("2147483647 + 0 = %d, want 2147483647", r) + } + y = 1 + r = x + y + if r != -2147483648 { + t.Errorf("2147483647 + 1 = %d, want -2147483648", r) + } + y = 2147483647 + r = x + y + if r != -2 { + t.Errorf("2147483647 + 2147483647 = %d, want -2", r) + } +} +func TestConstFoldint32sub(t *testing.T) { + var x, y, r int32 + x = -2147483648 + y = -2147483648 + r = x - y + if r != 0 { + t.Errorf("-2147483648 - -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x - y + if r != -1 { + t.Errorf("-2147483648 - -2147483647 = %d, want -1", r) + } + y = -1 + r = x - y + if r != -2147483647 { + t.Errorf("-2147483648 - -1 = %d, want -2147483647", r) + } + y = 0 + r = x - y + if r != -2147483648 { + t.Errorf("-2147483648 - 0 = %d, want -2147483648", r) + } + y = 1 + r = x - y + if r != 2147483647 { + t.Errorf("-2147483648 - 1 = %d, want 2147483647", r) + } + y = 2147483647 + r = x - y + if r != 1 { + t.Errorf("-2147483648 - 2147483647 = %d, want 1", r) + } + x = -2147483647 + y = -2147483648 + r = x - y + if r != 1 { + t.Errorf("-2147483647 - -2147483648 = %d, want 1", r) + } + y = -2147483647 + r = x - y + if r != 0 { + t.Errorf("-2147483647 - -2147483647 = %d, want 0", r) + } + y = -1 + r = x - y + if r != -2147483646 { + t.Errorf("-2147483647 - -1 = %d, want -2147483646", r) + } + y = 0 + r = x - y + if r != -2147483647 { + t.Errorf("-2147483647 - 0 = %d, want -2147483647", r) + } + y = 1 + r = x - y + if r != -2147483648 { + t.Errorf("-2147483647 - 1 = %d, want -2147483648", r) + } + y = 2147483647 + r = x - y + if r != 2 { + t.Errorf("-2147483647 - 2147483647 = %d, want 2", r) + } + x = -1 + y = -2147483648 + r = x - y + if r != 2147483647 { + t.Errorf("-1 - -2147483648 = %d, want 2147483647", r) + } + y = -2147483647 + r = x - y + if r != 2147483646 { + t.Errorf("-1 - -2147483647 = %d, want 2147483646", r) + } + y = -1 + r = x - y + if r != 0 { + t.Errorf("-1 - -1 = %d, want 0", r) + } + y = 0 + r = x - y + if r != -1 { + t.Errorf("-1 - 0 = %d, want -1", r) + } + y = 1 + r = x - y + if r != -2 { + t.Errorf("-1 - 1 = %d, want -2", r) + } + y = 2147483647 + r = x - y + if r != -2147483648 { + t.Errorf("-1 - 2147483647 = %d, want -2147483648", r) + } + x = 0 + y = -2147483648 + r = x - y + if r != -2147483648 { + t.Errorf("0 - -2147483648 = %d, want -2147483648", r) + } + y = -2147483647 + r = x - y + if r != 2147483647 { + t.Errorf("0 - -2147483647 = %d, want 2147483647", r) + } + y = -1 + r = x - y + if r != 1 { + t.Errorf("0 - -1 = %d, want 1", r) + } + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != -1 { + t.Errorf("0 - 1 = %d, want -1", r) + } + y = 2147483647 + r = x - y + if r != -2147483647 { + t.Errorf("0 - 2147483647 = %d, want -2147483647", r) + } + x = 1 + y = -2147483648 + r = x - y + if r != -2147483647 { + t.Errorf("1 - -2147483648 = %d, want -2147483647", r) + } + y = -2147483647 + r = x - y + if r != -2147483648 { + t.Errorf("1 - -2147483647 = %d, want -2147483648", r) + } + y = -1 + r = x - y + if r != 2 { + t.Errorf("1 - -1 = %d, want 2", r) + } + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 2147483647 + r = x - y + if r != -2147483646 { + t.Errorf("1 - 2147483647 = %d, want -2147483646", r) + } + x = 2147483647 + y = -2147483648 + r = x - y + if r != -1 { + t.Errorf("2147483647 - -2147483648 = %d, want -1", r) + } + y = -2147483647 + r = x - y + if r != -2 { + t.Errorf("2147483647 - -2147483647 = %d, want -2", r) + } + y = -1 + r = x - y + if r != -2147483648 { + t.Errorf("2147483647 - -1 = %d, want -2147483648", r) + } + y = 0 + r = x - y + if r != 2147483647 { + t.Errorf("2147483647 - 0 = %d, want 2147483647", r) + } + y = 1 + r = x - y + if r != 2147483646 { + t.Errorf("2147483647 - 1 = %d, want 2147483646", r) + } + y = 2147483647 + r = x - y + if r != 0 { + t.Errorf("2147483647 - 2147483647 = %d, want 0", r) + } +} +func TestConstFoldint32div(t *testing.T) { + var x, y, r int32 + x = -2147483648 + y = -2147483648 + r = x / y + if r != 1 { + t.Errorf("-2147483648 / -2147483648 = %d, want 1", r) + } + y = -2147483647 + r = x / y + if r != 1 { + t.Errorf("-2147483648 / -2147483647 = %d, want 1", r) + } + y = -1 + r = x / y + if r != -2147483648 { + t.Errorf("-2147483648 / -1 = %d, want -2147483648", r) + } + y = 1 + r = x / y + if r != -2147483648 { + t.Errorf("-2147483648 / 1 = %d, want -2147483648", r) + } + y = 2147483647 + r = x / y + if r != -1 { + t.Errorf("-2147483648 / 2147483647 = %d, want -1", r) + } + x = -2147483647 + y = -2147483648 + r = x / y + if r != 0 { + t.Errorf("-2147483647 / -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x / y + if r != 1 { + t.Errorf("-2147483647 / -2147483647 = %d, want 1", r) + } + y = -1 + r = x / y + if r != 2147483647 { + t.Errorf("-2147483647 / -1 = %d, want 2147483647", r) + } + y = 1 + r = x / y + if r != -2147483647 { + t.Errorf("-2147483647 / 1 = %d, want -2147483647", r) + } + y = 2147483647 + r = x / y + if r != -1 { + t.Errorf("-2147483647 / 2147483647 = %d, want -1", r) + } + x = -1 + y = -2147483648 + r = x / y + if r != 0 { + t.Errorf("-1 / -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x / y + if r != 0 { + t.Errorf("-1 / -2147483647 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 1 { + t.Errorf("-1 / -1 = %d, want 1", r) + } + y = 1 + r = x / y + if r != -1 { + t.Errorf("-1 / 1 = %d, want -1", r) + } + y = 2147483647 + r = x / y + if r != 0 { + t.Errorf("-1 / 2147483647 = %d, want 0", r) + } + x = 0 + y = -2147483648 + r = x / y + if r != 0 { + t.Errorf("0 / -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x / y + if r != 0 { + t.Errorf("0 / -2147483647 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 0 { + t.Errorf("0 / -1 = %d, want 0", r) + } + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 2147483647 + r = x / y + if r != 0 { + t.Errorf("0 / 2147483647 = %d, want 0", r) + } + x = 1 + y = -2147483648 + r = x / y + if r != 0 { + t.Errorf("1 / -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x / y + if r != 0 { + t.Errorf("1 / -2147483647 = %d, want 0", r) + } + y = -1 + r = x / y + if r != -1 { + t.Errorf("1 / -1 = %d, want -1", r) + } + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 2147483647 + r = x / y + if r != 0 { + t.Errorf("1 / 2147483647 = %d, want 0", r) + } + x = 2147483647 + y = -2147483648 + r = x / y + if r != 0 { + t.Errorf("2147483647 / -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x / y + if r != -1 { + t.Errorf("2147483647 / -2147483647 = %d, want -1", r) + } + y = -1 + r = x / y + if r != -2147483647 { + t.Errorf("2147483647 / -1 = %d, want -2147483647", r) + } + y = 1 + r = x / y + if r != 2147483647 { + t.Errorf("2147483647 / 1 = %d, want 2147483647", r) + } + y = 2147483647 + r = x / y + if r != 1 { + t.Errorf("2147483647 / 2147483647 = %d, want 1", r) + } +} +func TestConstFoldint32mul(t *testing.T) { + var x, y, r int32 + x = -2147483648 + y = -2147483648 + r = x * y + if r != 0 { + t.Errorf("-2147483648 * -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x * y + if r != -2147483648 { + t.Errorf("-2147483648 * -2147483647 = %d, want -2147483648", r) + } + y = -1 + r = x * y + if r != -2147483648 { + t.Errorf("-2147483648 * -1 = %d, want -2147483648", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-2147483648 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -2147483648 { + t.Errorf("-2147483648 * 1 = %d, want -2147483648", r) + } + y = 2147483647 + r = x * y + if r != -2147483648 { + t.Errorf("-2147483648 * 2147483647 = %d, want -2147483648", r) + } + x = -2147483647 + y = -2147483648 + r = x * y + if r != -2147483648 { + t.Errorf("-2147483647 * -2147483648 = %d, want -2147483648", r) + } + y = -2147483647 + r = x * y + if r != 1 { + t.Errorf("-2147483647 * -2147483647 = %d, want 1", r) + } + y = -1 + r = x * y + if r != 2147483647 { + t.Errorf("-2147483647 * -1 = %d, want 2147483647", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-2147483647 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -2147483647 { + t.Errorf("-2147483647 * 1 = %d, want -2147483647", r) + } + y = 2147483647 + r = x * y + if r != -1 { + t.Errorf("-2147483647 * 2147483647 = %d, want -1", r) + } + x = -1 + y = -2147483648 + r = x * y + if r != -2147483648 { + t.Errorf("-1 * -2147483648 = %d, want -2147483648", r) + } + y = -2147483647 + r = x * y + if r != 2147483647 { + t.Errorf("-1 * -2147483647 = %d, want 2147483647", r) + } + y = -1 + r = x * y + if r != 1 { + t.Errorf("-1 * -1 = %d, want 1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -1 { + t.Errorf("-1 * 1 = %d, want -1", r) + } + y = 2147483647 + r = x * y + if r != -2147483647 { + t.Errorf("-1 * 2147483647 = %d, want -2147483647", r) + } + x = 0 + y = -2147483648 + r = x * y + if r != 0 { + t.Errorf("0 * -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x * y + if r != 0 { + t.Errorf("0 * -2147483647 = %d, want 0", r) + } + y = -1 + r = x * y + if r != 0 { + t.Errorf("0 * -1 = %d, want 0", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 2147483647 + r = x * y + if r != 0 { + t.Errorf("0 * 2147483647 = %d, want 0", r) + } + x = 1 + y = -2147483648 + r = x * y + if r != -2147483648 { + t.Errorf("1 * -2147483648 = %d, want -2147483648", r) + } + y = -2147483647 + r = x * y + if r != -2147483647 { + t.Errorf("1 * -2147483647 = %d, want -2147483647", r) + } + y = -1 + r = x * y + if r != -1 { + t.Errorf("1 * -1 = %d, want -1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 2147483647 + r = x * y + if r != 2147483647 { + t.Errorf("1 * 2147483647 = %d, want 2147483647", r) + } + x = 2147483647 + y = -2147483648 + r = x * y + if r != -2147483648 { + t.Errorf("2147483647 * -2147483648 = %d, want -2147483648", r) + } + y = -2147483647 + r = x * y + if r != -1 { + t.Errorf("2147483647 * -2147483647 = %d, want -1", r) + } + y = -1 + r = x * y + if r != -2147483647 { + t.Errorf("2147483647 * -1 = %d, want -2147483647", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("2147483647 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 2147483647 { + t.Errorf("2147483647 * 1 = %d, want 2147483647", r) + } + y = 2147483647 + r = x * y + if r != 1 { + t.Errorf("2147483647 * 2147483647 = %d, want 1", r) + } +} +func TestConstFoldint32mod(t *testing.T) { + var x, y, r int32 + x = -2147483648 + y = -2147483648 + r = x % y + if r != 0 { + t.Errorf("-2147483648 % -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x % y + if r != -1 { + t.Errorf("-2147483648 % -2147483647 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-2147483648 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-2147483648 % 1 = %d, want 0", r) + } + y = 2147483647 + r = x % y + if r != -1 { + t.Errorf("-2147483648 % 2147483647 = %d, want -1", r) + } + x = -2147483647 + y = -2147483648 + r = x % y + if r != -2147483647 { + t.Errorf("-2147483647 % -2147483648 = %d, want -2147483647", r) + } + y = -2147483647 + r = x % y + if r != 0 { + t.Errorf("-2147483647 % -2147483647 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-2147483647 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-2147483647 % 1 = %d, want 0", r) + } + y = 2147483647 + r = x % y + if r != 0 { + t.Errorf("-2147483647 % 2147483647 = %d, want 0", r) + } + x = -1 + y = -2147483648 + r = x % y + if r != -1 { + t.Errorf("-1 % -2147483648 = %d, want -1", r) + } + y = -2147483647 + r = x % y + if r != -1 { + t.Errorf("-1 % -2147483647 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-1 % 1 = %d, want 0", r) + } + y = 2147483647 + r = x % y + if r != -1 { + t.Errorf("-1 % 2147483647 = %d, want -1", r) + } + x = 0 + y = -2147483648 + r = x % y + if r != 0 { + t.Errorf("0 % -2147483648 = %d, want 0", r) + } + y = -2147483647 + r = x % y + if r != 0 { + t.Errorf("0 % -2147483647 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("0 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 2147483647 + r = x % y + if r != 0 { + t.Errorf("0 % 2147483647 = %d, want 0", r) + } + x = 1 + y = -2147483648 + r = x % y + if r != 1 { + t.Errorf("1 % -2147483648 = %d, want 1", r) + } + y = -2147483647 + r = x % y + if r != 1 { + t.Errorf("1 % -2147483647 = %d, want 1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 2147483647 + r = x % y + if r != 1 { + t.Errorf("1 % 2147483647 = %d, want 1", r) + } + x = 2147483647 + y = -2147483648 + r = x % y + if r != 2147483647 { + t.Errorf("2147483647 % -2147483648 = %d, want 2147483647", r) + } + y = -2147483647 + r = x % y + if r != 0 { + t.Errorf("2147483647 % -2147483647 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("2147483647 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("2147483647 % 1 = %d, want 0", r) + } + y = 2147483647 + r = x % y + if r != 0 { + t.Errorf("2147483647 % 2147483647 = %d, want 0", r) + } +} +func TestConstFolduint16add(t *testing.T) { + var x, y, r uint16 + x = 0 + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 65535 + r = x + y + if r != 65535 { + t.Errorf("0 + 65535 = %d, want 65535", r) + } + x = 1 + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 65535 + r = x + y + if r != 0 { + t.Errorf("1 + 65535 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x + y + if r != 65535 { + t.Errorf("65535 + 0 = %d, want 65535", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("65535 + 1 = %d, want 0", r) + } + y = 65535 + r = x + y + if r != 65534 { + t.Errorf("65535 + 65535 = %d, want 65534", r) + } +} +func TestConstFolduint16sub(t *testing.T) { + var x, y, r uint16 + x = 0 + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != 65535 { + t.Errorf("0 - 1 = %d, want 65535", r) + } + y = 65535 + r = x - y + if r != 1 { + t.Errorf("0 - 65535 = %d, want 1", r) + } + x = 1 + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 65535 + r = x - y + if r != 2 { + t.Errorf("1 - 65535 = %d, want 2", r) + } + x = 65535 + y = 0 + r = x - y + if r != 65535 { + t.Errorf("65535 - 0 = %d, want 65535", r) + } + y = 1 + r = x - y + if r != 65534 { + t.Errorf("65535 - 1 = %d, want 65534", r) + } + y = 65535 + r = x - y + if r != 0 { + t.Errorf("65535 - 65535 = %d, want 0", r) + } +} +func TestConstFolduint16div(t *testing.T) { + var x, y, r uint16 + x = 0 + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 65535 + r = x / y + if r != 0 { + t.Errorf("0 / 65535 = %d, want 0", r) + } + x = 1 + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 65535 + r = x / y + if r != 0 { + t.Errorf("1 / 65535 = %d, want 0", r) + } + x = 65535 + y = 1 + r = x / y + if r != 65535 { + t.Errorf("65535 / 1 = %d, want 65535", r) + } + y = 65535 + r = x / y + if r != 1 { + t.Errorf("65535 / 65535 = %d, want 1", r) + } +} +func TestConstFolduint16mul(t *testing.T) { + var x, y, r uint16 + x = 0 + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 65535 + r = x * y + if r != 0 { + t.Errorf("0 * 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 65535 + r = x * y + if r != 65535 { + t.Errorf("1 * 65535 = %d, want 65535", r) + } + x = 65535 + y = 0 + r = x * y + if r != 0 { + t.Errorf("65535 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 65535 { + t.Errorf("65535 * 1 = %d, want 65535", r) + } + y = 65535 + r = x * y + if r != 1 { + t.Errorf("65535 * 65535 = %d, want 1", r) + } +} +func TestConstFolduint16mod(t *testing.T) { + var x, y, r uint16 + x = 0 + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 65535 + r = x % y + if r != 0 { + t.Errorf("0 % 65535 = %d, want 0", r) + } + x = 1 + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 65535 + r = x % y + if r != 1 { + t.Errorf("1 % 65535 = %d, want 1", r) + } + x = 65535 + y = 1 + r = x % y + if r != 0 { + t.Errorf("65535 % 1 = %d, want 0", r) + } + y = 65535 + r = x % y + if r != 0 { + t.Errorf("65535 % 65535 = %d, want 0", r) + } +} +func TestConstFoldint16add(t *testing.T) { + var x, y, r int16 + x = -32768 + y = -32768 + r = x + y + if r != 0 { + t.Errorf("-32768 + -32768 = %d, want 0", r) + } + y = -32767 + r = x + y + if r != 1 { + t.Errorf("-32768 + -32767 = %d, want 1", r) + } + y = -1 + r = x + y + if r != 32767 { + t.Errorf("-32768 + -1 = %d, want 32767", r) + } + y = 0 + r = x + y + if r != -32768 { + t.Errorf("-32768 + 0 = %d, want -32768", r) + } + y = 1 + r = x + y + if r != -32767 { + t.Errorf("-32768 + 1 = %d, want -32767", r) + } + y = 32766 + r = x + y + if r != -2 { + t.Errorf("-32768 + 32766 = %d, want -2", r) + } + y = 32767 + r = x + y + if r != -1 { + t.Errorf("-32768 + 32767 = %d, want -1", r) + } + x = -32767 + y = -32768 + r = x + y + if r != 1 { + t.Errorf("-32767 + -32768 = %d, want 1", r) + } + y = -32767 + r = x + y + if r != 2 { + t.Errorf("-32767 + -32767 = %d, want 2", r) + } + y = -1 + r = x + y + if r != -32768 { + t.Errorf("-32767 + -1 = %d, want -32768", r) + } + y = 0 + r = x + y + if r != -32767 { + t.Errorf("-32767 + 0 = %d, want -32767", r) + } + y = 1 + r = x + y + if r != -32766 { + t.Errorf("-32767 + 1 = %d, want -32766", r) + } + y = 32766 + r = x + y + if r != -1 { + t.Errorf("-32767 + 32766 = %d, want -1", r) + } + y = 32767 + r = x + y + if r != 0 { + t.Errorf("-32767 + 32767 = %d, want 0", r) + } + x = -1 + y = -32768 + r = x + y + if r != 32767 { + t.Errorf("-1 + -32768 = %d, want 32767", r) + } + y = -32767 + r = x + y + if r != -32768 { + t.Errorf("-1 + -32767 = %d, want -32768", r) + } + y = -1 + r = x + y + if r != -2 { + t.Errorf("-1 + -1 = %d, want -2", r) + } + y = 0 + r = x + y + if r != -1 { + t.Errorf("-1 + 0 = %d, want -1", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("-1 + 1 = %d, want 0", r) + } + y = 32766 + r = x + y + if r != 32765 { + t.Errorf("-1 + 32766 = %d, want 32765", r) + } + y = 32767 + r = x + y + if r != 32766 { + t.Errorf("-1 + 32767 = %d, want 32766", r) + } + x = 0 + y = -32768 + r = x + y + if r != -32768 { + t.Errorf("0 + -32768 = %d, want -32768", r) + } + y = -32767 + r = x + y + if r != -32767 { + t.Errorf("0 + -32767 = %d, want -32767", r) + } + y = -1 + r = x + y + if r != -1 { + t.Errorf("0 + -1 = %d, want -1", r) + } + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 32766 + r = x + y + if r != 32766 { + t.Errorf("0 + 32766 = %d, want 32766", r) + } + y = 32767 + r = x + y + if r != 32767 { + t.Errorf("0 + 32767 = %d, want 32767", r) + } + x = 1 + y = -32768 + r = x + y + if r != -32767 { + t.Errorf("1 + -32768 = %d, want -32767", r) + } + y = -32767 + r = x + y + if r != -32766 { + t.Errorf("1 + -32767 = %d, want -32766", r) + } + y = -1 + r = x + y + if r != 0 { + t.Errorf("1 + -1 = %d, want 0", r) + } + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 32766 + r = x + y + if r != 32767 { + t.Errorf("1 + 32766 = %d, want 32767", r) + } + y = 32767 + r = x + y + if r != -32768 { + t.Errorf("1 + 32767 = %d, want -32768", r) + } + x = 32766 + y = -32768 + r = x + y + if r != -2 { + t.Errorf("32766 + -32768 = %d, want -2", r) + } + y = -32767 + r = x + y + if r != -1 { + t.Errorf("32766 + -32767 = %d, want -1", r) + } + y = -1 + r = x + y + if r != 32765 { + t.Errorf("32766 + -1 = %d, want 32765", r) + } + y = 0 + r = x + y + if r != 32766 { + t.Errorf("32766 + 0 = %d, want 32766", r) + } + y = 1 + r = x + y + if r != 32767 { + t.Errorf("32766 + 1 = %d, want 32767", r) + } + y = 32766 + r = x + y + if r != -4 { + t.Errorf("32766 + 32766 = %d, want -4", r) + } + y = 32767 + r = x + y + if r != -3 { + t.Errorf("32766 + 32767 = %d, want -3", r) + } + x = 32767 + y = -32768 + r = x + y + if r != -1 { + t.Errorf("32767 + -32768 = %d, want -1", r) + } + y = -32767 + r = x + y + if r != 0 { + t.Errorf("32767 + -32767 = %d, want 0", r) + } + y = -1 + r = x + y + if r != 32766 { + t.Errorf("32767 + -1 = %d, want 32766", r) + } + y = 0 + r = x + y + if r != 32767 { + t.Errorf("32767 + 0 = %d, want 32767", r) + } + y = 1 + r = x + y + if r != -32768 { + t.Errorf("32767 + 1 = %d, want -32768", r) + } + y = 32766 + r = x + y + if r != -3 { + t.Errorf("32767 + 32766 = %d, want -3", r) + } + y = 32767 + r = x + y + if r != -2 { + t.Errorf("32767 + 32767 = %d, want -2", r) + } +} +func TestConstFoldint16sub(t *testing.T) { + var x, y, r int16 + x = -32768 + y = -32768 + r = x - y + if r != 0 { + t.Errorf("-32768 - -32768 = %d, want 0", r) + } + y = -32767 + r = x - y + if r != -1 { + t.Errorf("-32768 - -32767 = %d, want -1", r) + } + y = -1 + r = x - y + if r != -32767 { + t.Errorf("-32768 - -1 = %d, want -32767", r) + } + y = 0 + r = x - y + if r != -32768 { + t.Errorf("-32768 - 0 = %d, want -32768", r) + } + y = 1 + r = x - y + if r != 32767 { + t.Errorf("-32768 - 1 = %d, want 32767", r) + } + y = 32766 + r = x - y + if r != 2 { + t.Errorf("-32768 - 32766 = %d, want 2", r) + } + y = 32767 + r = x - y + if r != 1 { + t.Errorf("-32768 - 32767 = %d, want 1", r) + } + x = -32767 + y = -32768 + r = x - y + if r != 1 { + t.Errorf("-32767 - -32768 = %d, want 1", r) + } + y = -32767 + r = x - y + if r != 0 { + t.Errorf("-32767 - -32767 = %d, want 0", r) + } + y = -1 + r = x - y + if r != -32766 { + t.Errorf("-32767 - -1 = %d, want -32766", r) + } + y = 0 + r = x - y + if r != -32767 { + t.Errorf("-32767 - 0 = %d, want -32767", r) + } + y = 1 + r = x - y + if r != -32768 { + t.Errorf("-32767 - 1 = %d, want -32768", r) + } + y = 32766 + r = x - y + if r != 3 { + t.Errorf("-32767 - 32766 = %d, want 3", r) + } + y = 32767 + r = x - y + if r != 2 { + t.Errorf("-32767 - 32767 = %d, want 2", r) + } + x = -1 + y = -32768 + r = x - y + if r != 32767 { + t.Errorf("-1 - -32768 = %d, want 32767", r) + } + y = -32767 + r = x - y + if r != 32766 { + t.Errorf("-1 - -32767 = %d, want 32766", r) + } + y = -1 + r = x - y + if r != 0 { + t.Errorf("-1 - -1 = %d, want 0", r) + } + y = 0 + r = x - y + if r != -1 { + t.Errorf("-1 - 0 = %d, want -1", r) + } + y = 1 + r = x - y + if r != -2 { + t.Errorf("-1 - 1 = %d, want -2", r) + } + y = 32766 + r = x - y + if r != -32767 { + t.Errorf("-1 - 32766 = %d, want -32767", r) + } + y = 32767 + r = x - y + if r != -32768 { + t.Errorf("-1 - 32767 = %d, want -32768", r) + } + x = 0 + y = -32768 + r = x - y + if r != -32768 { + t.Errorf("0 - -32768 = %d, want -32768", r) + } + y = -32767 + r = x - y + if r != 32767 { + t.Errorf("0 - -32767 = %d, want 32767", r) + } + y = -1 + r = x - y + if r != 1 { + t.Errorf("0 - -1 = %d, want 1", r) + } + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != -1 { + t.Errorf("0 - 1 = %d, want -1", r) + } + y = 32766 + r = x - y + if r != -32766 { + t.Errorf("0 - 32766 = %d, want -32766", r) + } + y = 32767 + r = x - y + if r != -32767 { + t.Errorf("0 - 32767 = %d, want -32767", r) + } + x = 1 + y = -32768 + r = x - y + if r != -32767 { + t.Errorf("1 - -32768 = %d, want -32767", r) + } + y = -32767 + r = x - y + if r != -32768 { + t.Errorf("1 - -32767 = %d, want -32768", r) + } + y = -1 + r = x - y + if r != 2 { + t.Errorf("1 - -1 = %d, want 2", r) + } + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 32766 + r = x - y + if r != -32765 { + t.Errorf("1 - 32766 = %d, want -32765", r) + } + y = 32767 + r = x - y + if r != -32766 { + t.Errorf("1 - 32767 = %d, want -32766", r) + } + x = 32766 + y = -32768 + r = x - y + if r != -2 { + t.Errorf("32766 - -32768 = %d, want -2", r) + } + y = -32767 + r = x - y + if r != -3 { + t.Errorf("32766 - -32767 = %d, want -3", r) + } + y = -1 + r = x - y + if r != 32767 { + t.Errorf("32766 - -1 = %d, want 32767", r) + } + y = 0 + r = x - y + if r != 32766 { + t.Errorf("32766 - 0 = %d, want 32766", r) + } + y = 1 + r = x - y + if r != 32765 { + t.Errorf("32766 - 1 = %d, want 32765", r) + } + y = 32766 + r = x - y + if r != 0 { + t.Errorf("32766 - 32766 = %d, want 0", r) + } + y = 32767 + r = x - y + if r != -1 { + t.Errorf("32766 - 32767 = %d, want -1", r) + } + x = 32767 + y = -32768 + r = x - y + if r != -1 { + t.Errorf("32767 - -32768 = %d, want -1", r) + } + y = -32767 + r = x - y + if r != -2 { + t.Errorf("32767 - -32767 = %d, want -2", r) + } + y = -1 + r = x - y + if r != -32768 { + t.Errorf("32767 - -1 = %d, want -32768", r) + } + y = 0 + r = x - y + if r != 32767 { + t.Errorf("32767 - 0 = %d, want 32767", r) + } + y = 1 + r = x - y + if r != 32766 { + t.Errorf("32767 - 1 = %d, want 32766", r) + } + y = 32766 + r = x - y + if r != 1 { + t.Errorf("32767 - 32766 = %d, want 1", r) + } + y = 32767 + r = x - y + if r != 0 { + t.Errorf("32767 - 32767 = %d, want 0", r) + } +} +func TestConstFoldint16div(t *testing.T) { + var x, y, r int16 + x = -32768 + y = -32768 + r = x / y + if r != 1 { + t.Errorf("-32768 / -32768 = %d, want 1", r) + } + y = -32767 + r = x / y + if r != 1 { + t.Errorf("-32768 / -32767 = %d, want 1", r) + } + y = -1 + r = x / y + if r != -32768 { + t.Errorf("-32768 / -1 = %d, want -32768", r) + } + y = 1 + r = x / y + if r != -32768 { + t.Errorf("-32768 / 1 = %d, want -32768", r) + } + y = 32766 + r = x / y + if r != -1 { + t.Errorf("-32768 / 32766 = %d, want -1", r) + } + y = 32767 + r = x / y + if r != -1 { + t.Errorf("-32768 / 32767 = %d, want -1", r) + } + x = -32767 + y = -32768 + r = x / y + if r != 0 { + t.Errorf("-32767 / -32768 = %d, want 0", r) + } + y = -32767 + r = x / y + if r != 1 { + t.Errorf("-32767 / -32767 = %d, want 1", r) + } + y = -1 + r = x / y + if r != 32767 { + t.Errorf("-32767 / -1 = %d, want 32767", r) + } + y = 1 + r = x / y + if r != -32767 { + t.Errorf("-32767 / 1 = %d, want -32767", r) + } + y = 32766 + r = x / y + if r != -1 { + t.Errorf("-32767 / 32766 = %d, want -1", r) + } + y = 32767 + r = x / y + if r != -1 { + t.Errorf("-32767 / 32767 = %d, want -1", r) + } + x = -1 + y = -32768 + r = x / y + if r != 0 { + t.Errorf("-1 / -32768 = %d, want 0", r) + } + y = -32767 + r = x / y + if r != 0 { + t.Errorf("-1 / -32767 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 1 { + t.Errorf("-1 / -1 = %d, want 1", r) + } + y = 1 + r = x / y + if r != -1 { + t.Errorf("-1 / 1 = %d, want -1", r) + } + y = 32766 + r = x / y + if r != 0 { + t.Errorf("-1 / 32766 = %d, want 0", r) + } + y = 32767 + r = x / y + if r != 0 { + t.Errorf("-1 / 32767 = %d, want 0", r) + } + x = 0 + y = -32768 + r = x / y + if r != 0 { + t.Errorf("0 / -32768 = %d, want 0", r) + } + y = -32767 + r = x / y + if r != 0 { + t.Errorf("0 / -32767 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 0 { + t.Errorf("0 / -1 = %d, want 0", r) + } + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 32766 + r = x / y + if r != 0 { + t.Errorf("0 / 32766 = %d, want 0", r) + } + y = 32767 + r = x / y + if r != 0 { + t.Errorf("0 / 32767 = %d, want 0", r) + } + x = 1 + y = -32768 + r = x / y + if r != 0 { + t.Errorf("1 / -32768 = %d, want 0", r) + } + y = -32767 + r = x / y + if r != 0 { + t.Errorf("1 / -32767 = %d, want 0", r) + } + y = -1 + r = x / y + if r != -1 { + t.Errorf("1 / -1 = %d, want -1", r) + } + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 32766 + r = x / y + if r != 0 { + t.Errorf("1 / 32766 = %d, want 0", r) + } + y = 32767 + r = x / y + if r != 0 { + t.Errorf("1 / 32767 = %d, want 0", r) + } + x = 32766 + y = -32768 + r = x / y + if r != 0 { + t.Errorf("32766 / -32768 = %d, want 0", r) + } + y = -32767 + r = x / y + if r != 0 { + t.Errorf("32766 / -32767 = %d, want 0", r) + } + y = -1 + r = x / y + if r != -32766 { + t.Errorf("32766 / -1 = %d, want -32766", r) + } + y = 1 + r = x / y + if r != 32766 { + t.Errorf("32766 / 1 = %d, want 32766", r) + } + y = 32766 + r = x / y + if r != 1 { + t.Errorf("32766 / 32766 = %d, want 1", r) + } + y = 32767 + r = x / y + if r != 0 { + t.Errorf("32766 / 32767 = %d, want 0", r) + } + x = 32767 + y = -32768 + r = x / y + if r != 0 { + t.Errorf("32767 / -32768 = %d, want 0", r) + } + y = -32767 + r = x / y + if r != -1 { + t.Errorf("32767 / -32767 = %d, want -1", r) + } + y = -1 + r = x / y + if r != -32767 { + t.Errorf("32767 / -1 = %d, want -32767", r) + } + y = 1 + r = x / y + if r != 32767 { + t.Errorf("32767 / 1 = %d, want 32767", r) + } + y = 32766 + r = x / y + if r != 1 { + t.Errorf("32767 / 32766 = %d, want 1", r) + } + y = 32767 + r = x / y + if r != 1 { + t.Errorf("32767 / 32767 = %d, want 1", r) + } +} +func TestConstFoldint16mul(t *testing.T) { + var x, y, r int16 + x = -32768 + y = -32768 + r = x * y + if r != 0 { + t.Errorf("-32768 * -32768 = %d, want 0", r) + } + y = -32767 + r = x * y + if r != -32768 { + t.Errorf("-32768 * -32767 = %d, want -32768", r) + } + y = -1 + r = x * y + if r != -32768 { + t.Errorf("-32768 * -1 = %d, want -32768", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-32768 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -32768 { + t.Errorf("-32768 * 1 = %d, want -32768", r) + } + y = 32766 + r = x * y + if r != 0 { + t.Errorf("-32768 * 32766 = %d, want 0", r) + } + y = 32767 + r = x * y + if r != -32768 { + t.Errorf("-32768 * 32767 = %d, want -32768", r) + } + x = -32767 + y = -32768 + r = x * y + if r != -32768 { + t.Errorf("-32767 * -32768 = %d, want -32768", r) + } + y = -32767 + r = x * y + if r != 1 { + t.Errorf("-32767 * -32767 = %d, want 1", r) + } + y = -1 + r = x * y + if r != 32767 { + t.Errorf("-32767 * -1 = %d, want 32767", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-32767 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -32767 { + t.Errorf("-32767 * 1 = %d, want -32767", r) + } + y = 32766 + r = x * y + if r != 32766 { + t.Errorf("-32767 * 32766 = %d, want 32766", r) + } + y = 32767 + r = x * y + if r != -1 { + t.Errorf("-32767 * 32767 = %d, want -1", r) + } + x = -1 + y = -32768 + r = x * y + if r != -32768 { + t.Errorf("-1 * -32768 = %d, want -32768", r) + } + y = -32767 + r = x * y + if r != 32767 { + t.Errorf("-1 * -32767 = %d, want 32767", r) + } + y = -1 + r = x * y + if r != 1 { + t.Errorf("-1 * -1 = %d, want 1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -1 { + t.Errorf("-1 * 1 = %d, want -1", r) + } + y = 32766 + r = x * y + if r != -32766 { + t.Errorf("-1 * 32766 = %d, want -32766", r) + } + y = 32767 + r = x * y + if r != -32767 { + t.Errorf("-1 * 32767 = %d, want -32767", r) + } + x = 0 + y = -32768 + r = x * y + if r != 0 { + t.Errorf("0 * -32768 = %d, want 0", r) + } + y = -32767 + r = x * y + if r != 0 { + t.Errorf("0 * -32767 = %d, want 0", r) + } + y = -1 + r = x * y + if r != 0 { + t.Errorf("0 * -1 = %d, want 0", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 32766 + r = x * y + if r != 0 { + t.Errorf("0 * 32766 = %d, want 0", r) + } + y = 32767 + r = x * y + if r != 0 { + t.Errorf("0 * 32767 = %d, want 0", r) + } + x = 1 + y = -32768 + r = x * y + if r != -32768 { + t.Errorf("1 * -32768 = %d, want -32768", r) + } + y = -32767 + r = x * y + if r != -32767 { + t.Errorf("1 * -32767 = %d, want -32767", r) + } + y = -1 + r = x * y + if r != -1 { + t.Errorf("1 * -1 = %d, want -1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 32766 + r = x * y + if r != 32766 { + t.Errorf("1 * 32766 = %d, want 32766", r) + } + y = 32767 + r = x * y + if r != 32767 { + t.Errorf("1 * 32767 = %d, want 32767", r) + } + x = 32766 + y = -32768 + r = x * y + if r != 0 { + t.Errorf("32766 * -32768 = %d, want 0", r) + } + y = -32767 + r = x * y + if r != 32766 { + t.Errorf("32766 * -32767 = %d, want 32766", r) + } + y = -1 + r = x * y + if r != -32766 { + t.Errorf("32766 * -1 = %d, want -32766", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("32766 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 32766 { + t.Errorf("32766 * 1 = %d, want 32766", r) + } + y = 32766 + r = x * y + if r != 4 { + t.Errorf("32766 * 32766 = %d, want 4", r) + } + y = 32767 + r = x * y + if r != -32766 { + t.Errorf("32766 * 32767 = %d, want -32766", r) + } + x = 32767 + y = -32768 + r = x * y + if r != -32768 { + t.Errorf("32767 * -32768 = %d, want -32768", r) + } + y = -32767 + r = x * y + if r != -1 { + t.Errorf("32767 * -32767 = %d, want -1", r) + } + y = -1 + r = x * y + if r != -32767 { + t.Errorf("32767 * -1 = %d, want -32767", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("32767 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 32767 { + t.Errorf("32767 * 1 = %d, want 32767", r) + } + y = 32766 + r = x * y + if r != -32766 { + t.Errorf("32767 * 32766 = %d, want -32766", r) + } + y = 32767 + r = x * y + if r != 1 { + t.Errorf("32767 * 32767 = %d, want 1", r) + } +} +func TestConstFoldint16mod(t *testing.T) { + var x, y, r int16 + x = -32768 + y = -32768 + r = x % y + if r != 0 { + t.Errorf("-32768 % -32768 = %d, want 0", r) + } + y = -32767 + r = x % y + if r != -1 { + t.Errorf("-32768 % -32767 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-32768 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-32768 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != -2 { + t.Errorf("-32768 % 32766 = %d, want -2", r) + } + y = 32767 + r = x % y + if r != -1 { + t.Errorf("-32768 % 32767 = %d, want -1", r) + } + x = -32767 + y = -32768 + r = x % y + if r != -32767 { + t.Errorf("-32767 % -32768 = %d, want -32767", r) + } + y = -32767 + r = x % y + if r != 0 { + t.Errorf("-32767 % -32767 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-32767 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-32767 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != -1 { + t.Errorf("-32767 % 32766 = %d, want -1", r) + } + y = 32767 + r = x % y + if r != 0 { + t.Errorf("-32767 % 32767 = %d, want 0", r) + } + x = -1 + y = -32768 + r = x % y + if r != -1 { + t.Errorf("-1 % -32768 = %d, want -1", r) + } + y = -32767 + r = x % y + if r != -1 { + t.Errorf("-1 % -32767 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-1 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != -1 { + t.Errorf("-1 % 32766 = %d, want -1", r) + } + y = 32767 + r = x % y + if r != -1 { + t.Errorf("-1 % 32767 = %d, want -1", r) + } + x = 0 + y = -32768 + r = x % y + if r != 0 { + t.Errorf("0 % -32768 = %d, want 0", r) + } + y = -32767 + r = x % y + if r != 0 { + t.Errorf("0 % -32767 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("0 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != 0 { + t.Errorf("0 % 32766 = %d, want 0", r) + } + y = 32767 + r = x % y + if r != 0 { + t.Errorf("0 % 32767 = %d, want 0", r) + } + x = 1 + y = -32768 + r = x % y + if r != 1 { + t.Errorf("1 % -32768 = %d, want 1", r) + } + y = -32767 + r = x % y + if r != 1 { + t.Errorf("1 % -32767 = %d, want 1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != 1 { + t.Errorf("1 % 32766 = %d, want 1", r) + } + y = 32767 + r = x % y + if r != 1 { + t.Errorf("1 % 32767 = %d, want 1", r) + } + x = 32766 + y = -32768 + r = x % y + if r != 32766 { + t.Errorf("32766 % -32768 = %d, want 32766", r) + } + y = -32767 + r = x % y + if r != 32766 { + t.Errorf("32766 % -32767 = %d, want 32766", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("32766 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("32766 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != 0 { + t.Errorf("32766 % 32766 = %d, want 0", r) + } + y = 32767 + r = x % y + if r != 32766 { + t.Errorf("32766 % 32767 = %d, want 32766", r) + } + x = 32767 + y = -32768 + r = x % y + if r != 32767 { + t.Errorf("32767 % -32768 = %d, want 32767", r) + } + y = -32767 + r = x % y + if r != 0 { + t.Errorf("32767 % -32767 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("32767 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("32767 % 1 = %d, want 0", r) + } + y = 32766 + r = x % y + if r != 1 { + t.Errorf("32767 % 32766 = %d, want 1", r) + } + y = 32767 + r = x % y + if r != 0 { + t.Errorf("32767 % 32767 = %d, want 0", r) + } +} +func TestConstFolduint8add(t *testing.T) { + var x, y, r uint8 + x = 0 + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 255 + r = x + y + if r != 255 { + t.Errorf("0 + 255 = %d, want 255", r) + } + x = 1 + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 255 + r = x + y + if r != 0 { + t.Errorf("1 + 255 = %d, want 0", r) + } + x = 255 + y = 0 + r = x + y + if r != 255 { + t.Errorf("255 + 0 = %d, want 255", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("255 + 1 = %d, want 0", r) + } + y = 255 + r = x + y + if r != 254 { + t.Errorf("255 + 255 = %d, want 254", r) + } +} +func TestConstFolduint8sub(t *testing.T) { + var x, y, r uint8 + x = 0 + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != 255 { + t.Errorf("0 - 1 = %d, want 255", r) + } + y = 255 + r = x - y + if r != 1 { + t.Errorf("0 - 255 = %d, want 1", r) + } + x = 1 + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 255 + r = x - y + if r != 2 { + t.Errorf("1 - 255 = %d, want 2", r) + } + x = 255 + y = 0 + r = x - y + if r != 255 { + t.Errorf("255 - 0 = %d, want 255", r) + } + y = 1 + r = x - y + if r != 254 { + t.Errorf("255 - 1 = %d, want 254", r) + } + y = 255 + r = x - y + if r != 0 { + t.Errorf("255 - 255 = %d, want 0", r) + } +} +func TestConstFolduint8div(t *testing.T) { + var x, y, r uint8 + x = 0 + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 255 + r = x / y + if r != 0 { + t.Errorf("0 / 255 = %d, want 0", r) + } + x = 1 + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 255 + r = x / y + if r != 0 { + t.Errorf("1 / 255 = %d, want 0", r) + } + x = 255 + y = 1 + r = x / y + if r != 255 { + t.Errorf("255 / 1 = %d, want 255", r) + } + y = 255 + r = x / y + if r != 1 { + t.Errorf("255 / 255 = %d, want 1", r) + } +} +func TestConstFolduint8mul(t *testing.T) { + var x, y, r uint8 + x = 0 + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 255 + r = x * y + if r != 0 { + t.Errorf("0 * 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 255 + r = x * y + if r != 255 { + t.Errorf("1 * 255 = %d, want 255", r) + } + x = 255 + y = 0 + r = x * y + if r != 0 { + t.Errorf("255 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 255 { + t.Errorf("255 * 1 = %d, want 255", r) + } + y = 255 + r = x * y + if r != 1 { + t.Errorf("255 * 255 = %d, want 1", r) + } +} +func TestConstFolduint8mod(t *testing.T) { + var x, y, r uint8 + x = 0 + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 255 + r = x % y + if r != 0 { + t.Errorf("0 % 255 = %d, want 0", r) + } + x = 1 + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 255 + r = x % y + if r != 1 { + t.Errorf("1 % 255 = %d, want 1", r) + } + x = 255 + y = 1 + r = x % y + if r != 0 { + t.Errorf("255 % 1 = %d, want 0", r) + } + y = 255 + r = x % y + if r != 0 { + t.Errorf("255 % 255 = %d, want 0", r) + } +} +func TestConstFoldint8add(t *testing.T) { + var x, y, r int8 + x = -128 + y = -128 + r = x + y + if r != 0 { + t.Errorf("-128 + -128 = %d, want 0", r) + } + y = -127 + r = x + y + if r != 1 { + t.Errorf("-128 + -127 = %d, want 1", r) + } + y = -1 + r = x + y + if r != 127 { + t.Errorf("-128 + -1 = %d, want 127", r) + } + y = 0 + r = x + y + if r != -128 { + t.Errorf("-128 + 0 = %d, want -128", r) + } + y = 1 + r = x + y + if r != -127 { + t.Errorf("-128 + 1 = %d, want -127", r) + } + y = 126 + r = x + y + if r != -2 { + t.Errorf("-128 + 126 = %d, want -2", r) + } + y = 127 + r = x + y + if r != -1 { + t.Errorf("-128 + 127 = %d, want -1", r) + } + x = -127 + y = -128 + r = x + y + if r != 1 { + t.Errorf("-127 + -128 = %d, want 1", r) + } + y = -127 + r = x + y + if r != 2 { + t.Errorf("-127 + -127 = %d, want 2", r) + } + y = -1 + r = x + y + if r != -128 { + t.Errorf("-127 + -1 = %d, want -128", r) + } + y = 0 + r = x + y + if r != -127 { + t.Errorf("-127 + 0 = %d, want -127", r) + } + y = 1 + r = x + y + if r != -126 { + t.Errorf("-127 + 1 = %d, want -126", r) + } + y = 126 + r = x + y + if r != -1 { + t.Errorf("-127 + 126 = %d, want -1", r) + } + y = 127 + r = x + y + if r != 0 { + t.Errorf("-127 + 127 = %d, want 0", r) + } + x = -1 + y = -128 + r = x + y + if r != 127 { + t.Errorf("-1 + -128 = %d, want 127", r) + } + y = -127 + r = x + y + if r != -128 { + t.Errorf("-1 + -127 = %d, want -128", r) + } + y = -1 + r = x + y + if r != -2 { + t.Errorf("-1 + -1 = %d, want -2", r) + } + y = 0 + r = x + y + if r != -1 { + t.Errorf("-1 + 0 = %d, want -1", r) + } + y = 1 + r = x + y + if r != 0 { + t.Errorf("-1 + 1 = %d, want 0", r) + } + y = 126 + r = x + y + if r != 125 { + t.Errorf("-1 + 126 = %d, want 125", r) + } + y = 127 + r = x + y + if r != 126 { + t.Errorf("-1 + 127 = %d, want 126", r) + } + x = 0 + y = -128 + r = x + y + if r != -128 { + t.Errorf("0 + -128 = %d, want -128", r) + } + y = -127 + r = x + y + if r != -127 { + t.Errorf("0 + -127 = %d, want -127", r) + } + y = -1 + r = x + y + if r != -1 { + t.Errorf("0 + -1 = %d, want -1", r) + } + y = 0 + r = x + y + if r != 0 { + t.Errorf("0 + 0 = %d, want 0", r) + } + y = 1 + r = x + y + if r != 1 { + t.Errorf("0 + 1 = %d, want 1", r) + } + y = 126 + r = x + y + if r != 126 { + t.Errorf("0 + 126 = %d, want 126", r) + } + y = 127 + r = x + y + if r != 127 { + t.Errorf("0 + 127 = %d, want 127", r) + } + x = 1 + y = -128 + r = x + y + if r != -127 { + t.Errorf("1 + -128 = %d, want -127", r) + } + y = -127 + r = x + y + if r != -126 { + t.Errorf("1 + -127 = %d, want -126", r) + } + y = -1 + r = x + y + if r != 0 { + t.Errorf("1 + -1 = %d, want 0", r) + } + y = 0 + r = x + y + if r != 1 { + t.Errorf("1 + 0 = %d, want 1", r) + } + y = 1 + r = x + y + if r != 2 { + t.Errorf("1 + 1 = %d, want 2", r) + } + y = 126 + r = x + y + if r != 127 { + t.Errorf("1 + 126 = %d, want 127", r) + } + y = 127 + r = x + y + if r != -128 { + t.Errorf("1 + 127 = %d, want -128", r) + } + x = 126 + y = -128 + r = x + y + if r != -2 { + t.Errorf("126 + -128 = %d, want -2", r) + } + y = -127 + r = x + y + if r != -1 { + t.Errorf("126 + -127 = %d, want -1", r) + } + y = -1 + r = x + y + if r != 125 { + t.Errorf("126 + -1 = %d, want 125", r) + } + y = 0 + r = x + y + if r != 126 { + t.Errorf("126 + 0 = %d, want 126", r) + } + y = 1 + r = x + y + if r != 127 { + t.Errorf("126 + 1 = %d, want 127", r) + } + y = 126 + r = x + y + if r != -4 { + t.Errorf("126 + 126 = %d, want -4", r) + } + y = 127 + r = x + y + if r != -3 { + t.Errorf("126 + 127 = %d, want -3", r) + } + x = 127 + y = -128 + r = x + y + if r != -1 { + t.Errorf("127 + -128 = %d, want -1", r) + } + y = -127 + r = x + y + if r != 0 { + t.Errorf("127 + -127 = %d, want 0", r) + } + y = -1 + r = x + y + if r != 126 { + t.Errorf("127 + -1 = %d, want 126", r) + } + y = 0 + r = x + y + if r != 127 { + t.Errorf("127 + 0 = %d, want 127", r) + } + y = 1 + r = x + y + if r != -128 { + t.Errorf("127 + 1 = %d, want -128", r) + } + y = 126 + r = x + y + if r != -3 { + t.Errorf("127 + 126 = %d, want -3", r) + } + y = 127 + r = x + y + if r != -2 { + t.Errorf("127 + 127 = %d, want -2", r) + } +} +func TestConstFoldint8sub(t *testing.T) { + var x, y, r int8 + x = -128 + y = -128 + r = x - y + if r != 0 { + t.Errorf("-128 - -128 = %d, want 0", r) + } + y = -127 + r = x - y + if r != -1 { + t.Errorf("-128 - -127 = %d, want -1", r) + } + y = -1 + r = x - y + if r != -127 { + t.Errorf("-128 - -1 = %d, want -127", r) + } + y = 0 + r = x - y + if r != -128 { + t.Errorf("-128 - 0 = %d, want -128", r) + } + y = 1 + r = x - y + if r != 127 { + t.Errorf("-128 - 1 = %d, want 127", r) + } + y = 126 + r = x - y + if r != 2 { + t.Errorf("-128 - 126 = %d, want 2", r) + } + y = 127 + r = x - y + if r != 1 { + t.Errorf("-128 - 127 = %d, want 1", r) + } + x = -127 + y = -128 + r = x - y + if r != 1 { + t.Errorf("-127 - -128 = %d, want 1", r) + } + y = -127 + r = x - y + if r != 0 { + t.Errorf("-127 - -127 = %d, want 0", r) + } + y = -1 + r = x - y + if r != -126 { + t.Errorf("-127 - -1 = %d, want -126", r) + } + y = 0 + r = x - y + if r != -127 { + t.Errorf("-127 - 0 = %d, want -127", r) + } + y = 1 + r = x - y + if r != -128 { + t.Errorf("-127 - 1 = %d, want -128", r) + } + y = 126 + r = x - y + if r != 3 { + t.Errorf("-127 - 126 = %d, want 3", r) + } + y = 127 + r = x - y + if r != 2 { + t.Errorf("-127 - 127 = %d, want 2", r) + } + x = -1 + y = -128 + r = x - y + if r != 127 { + t.Errorf("-1 - -128 = %d, want 127", r) + } + y = -127 + r = x - y + if r != 126 { + t.Errorf("-1 - -127 = %d, want 126", r) + } + y = -1 + r = x - y + if r != 0 { + t.Errorf("-1 - -1 = %d, want 0", r) + } + y = 0 + r = x - y + if r != -1 { + t.Errorf("-1 - 0 = %d, want -1", r) + } + y = 1 + r = x - y + if r != -2 { + t.Errorf("-1 - 1 = %d, want -2", r) + } + y = 126 + r = x - y + if r != -127 { + t.Errorf("-1 - 126 = %d, want -127", r) + } + y = 127 + r = x - y + if r != -128 { + t.Errorf("-1 - 127 = %d, want -128", r) + } + x = 0 + y = -128 + r = x - y + if r != -128 { + t.Errorf("0 - -128 = %d, want -128", r) + } + y = -127 + r = x - y + if r != 127 { + t.Errorf("0 - -127 = %d, want 127", r) + } + y = -1 + r = x - y + if r != 1 { + t.Errorf("0 - -1 = %d, want 1", r) + } + y = 0 + r = x - y + if r != 0 { + t.Errorf("0 - 0 = %d, want 0", r) + } + y = 1 + r = x - y + if r != -1 { + t.Errorf("0 - 1 = %d, want -1", r) + } + y = 126 + r = x - y + if r != -126 { + t.Errorf("0 - 126 = %d, want -126", r) + } + y = 127 + r = x - y + if r != -127 { + t.Errorf("0 - 127 = %d, want -127", r) + } + x = 1 + y = -128 + r = x - y + if r != -127 { + t.Errorf("1 - -128 = %d, want -127", r) + } + y = -127 + r = x - y + if r != -128 { + t.Errorf("1 - -127 = %d, want -128", r) + } + y = -1 + r = x - y + if r != 2 { + t.Errorf("1 - -1 = %d, want 2", r) + } + y = 0 + r = x - y + if r != 1 { + t.Errorf("1 - 0 = %d, want 1", r) + } + y = 1 + r = x - y + if r != 0 { + t.Errorf("1 - 1 = %d, want 0", r) + } + y = 126 + r = x - y + if r != -125 { + t.Errorf("1 - 126 = %d, want -125", r) + } + y = 127 + r = x - y + if r != -126 { + t.Errorf("1 - 127 = %d, want -126", r) + } + x = 126 + y = -128 + r = x - y + if r != -2 { + t.Errorf("126 - -128 = %d, want -2", r) + } + y = -127 + r = x - y + if r != -3 { + t.Errorf("126 - -127 = %d, want -3", r) + } + y = -1 + r = x - y + if r != 127 { + t.Errorf("126 - -1 = %d, want 127", r) + } + y = 0 + r = x - y + if r != 126 { + t.Errorf("126 - 0 = %d, want 126", r) + } + y = 1 + r = x - y + if r != 125 { + t.Errorf("126 - 1 = %d, want 125", r) + } + y = 126 + r = x - y + if r != 0 { + t.Errorf("126 - 126 = %d, want 0", r) + } + y = 127 + r = x - y + if r != -1 { + t.Errorf("126 - 127 = %d, want -1", r) + } + x = 127 + y = -128 + r = x - y + if r != -1 { + t.Errorf("127 - -128 = %d, want -1", r) + } + y = -127 + r = x - y + if r != -2 { + t.Errorf("127 - -127 = %d, want -2", r) + } + y = -1 + r = x - y + if r != -128 { + t.Errorf("127 - -1 = %d, want -128", r) + } + y = 0 + r = x - y + if r != 127 { + t.Errorf("127 - 0 = %d, want 127", r) + } + y = 1 + r = x - y + if r != 126 { + t.Errorf("127 - 1 = %d, want 126", r) + } + y = 126 + r = x - y + if r != 1 { + t.Errorf("127 - 126 = %d, want 1", r) + } + y = 127 + r = x - y + if r != 0 { + t.Errorf("127 - 127 = %d, want 0", r) + } +} +func TestConstFoldint8div(t *testing.T) { + var x, y, r int8 + x = -128 + y = -128 + r = x / y + if r != 1 { + t.Errorf("-128 / -128 = %d, want 1", r) + } + y = -127 + r = x / y + if r != 1 { + t.Errorf("-128 / -127 = %d, want 1", r) + } + y = -1 + r = x / y + if r != -128 { + t.Errorf("-128 / -1 = %d, want -128", r) + } + y = 1 + r = x / y + if r != -128 { + t.Errorf("-128 / 1 = %d, want -128", r) + } + y = 126 + r = x / y + if r != -1 { + t.Errorf("-128 / 126 = %d, want -1", r) + } + y = 127 + r = x / y + if r != -1 { + t.Errorf("-128 / 127 = %d, want -1", r) + } + x = -127 + y = -128 + r = x / y + if r != 0 { + t.Errorf("-127 / -128 = %d, want 0", r) + } + y = -127 + r = x / y + if r != 1 { + t.Errorf("-127 / -127 = %d, want 1", r) + } + y = -1 + r = x / y + if r != 127 { + t.Errorf("-127 / -1 = %d, want 127", r) + } + y = 1 + r = x / y + if r != -127 { + t.Errorf("-127 / 1 = %d, want -127", r) + } + y = 126 + r = x / y + if r != -1 { + t.Errorf("-127 / 126 = %d, want -1", r) + } + y = 127 + r = x / y + if r != -1 { + t.Errorf("-127 / 127 = %d, want -1", r) + } + x = -1 + y = -128 + r = x / y + if r != 0 { + t.Errorf("-1 / -128 = %d, want 0", r) + } + y = -127 + r = x / y + if r != 0 { + t.Errorf("-1 / -127 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 1 { + t.Errorf("-1 / -1 = %d, want 1", r) + } + y = 1 + r = x / y + if r != -1 { + t.Errorf("-1 / 1 = %d, want -1", r) + } + y = 126 + r = x / y + if r != 0 { + t.Errorf("-1 / 126 = %d, want 0", r) + } + y = 127 + r = x / y + if r != 0 { + t.Errorf("-1 / 127 = %d, want 0", r) + } + x = 0 + y = -128 + r = x / y + if r != 0 { + t.Errorf("0 / -128 = %d, want 0", r) + } + y = -127 + r = x / y + if r != 0 { + t.Errorf("0 / -127 = %d, want 0", r) + } + y = -1 + r = x / y + if r != 0 { + t.Errorf("0 / -1 = %d, want 0", r) + } + y = 1 + r = x / y + if r != 0 { + t.Errorf("0 / 1 = %d, want 0", r) + } + y = 126 + r = x / y + if r != 0 { + t.Errorf("0 / 126 = %d, want 0", r) + } + y = 127 + r = x / y + if r != 0 { + t.Errorf("0 / 127 = %d, want 0", r) + } + x = 1 + y = -128 + r = x / y + if r != 0 { + t.Errorf("1 / -128 = %d, want 0", r) + } + y = -127 + r = x / y + if r != 0 { + t.Errorf("1 / -127 = %d, want 0", r) + } + y = -1 + r = x / y + if r != -1 { + t.Errorf("1 / -1 = %d, want -1", r) + } + y = 1 + r = x / y + if r != 1 { + t.Errorf("1 / 1 = %d, want 1", r) + } + y = 126 + r = x / y + if r != 0 { + t.Errorf("1 / 126 = %d, want 0", r) + } + y = 127 + r = x / y + if r != 0 { + t.Errorf("1 / 127 = %d, want 0", r) + } + x = 126 + y = -128 + r = x / y + if r != 0 { + t.Errorf("126 / -128 = %d, want 0", r) + } + y = -127 + r = x / y + if r != 0 { + t.Errorf("126 / -127 = %d, want 0", r) + } + y = -1 + r = x / y + if r != -126 { + t.Errorf("126 / -1 = %d, want -126", r) + } + y = 1 + r = x / y + if r != 126 { + t.Errorf("126 / 1 = %d, want 126", r) + } + y = 126 + r = x / y + if r != 1 { + t.Errorf("126 / 126 = %d, want 1", r) + } + y = 127 + r = x / y + if r != 0 { + t.Errorf("126 / 127 = %d, want 0", r) + } + x = 127 + y = -128 + r = x / y + if r != 0 { + t.Errorf("127 / -128 = %d, want 0", r) + } + y = -127 + r = x / y + if r != -1 { + t.Errorf("127 / -127 = %d, want -1", r) + } + y = -1 + r = x / y + if r != -127 { + t.Errorf("127 / -1 = %d, want -127", r) + } + y = 1 + r = x / y + if r != 127 { + t.Errorf("127 / 1 = %d, want 127", r) + } + y = 126 + r = x / y + if r != 1 { + t.Errorf("127 / 126 = %d, want 1", r) + } + y = 127 + r = x / y + if r != 1 { + t.Errorf("127 / 127 = %d, want 1", r) + } +} +func TestConstFoldint8mul(t *testing.T) { + var x, y, r int8 + x = -128 + y = -128 + r = x * y + if r != 0 { + t.Errorf("-128 * -128 = %d, want 0", r) + } + y = -127 + r = x * y + if r != -128 { + t.Errorf("-128 * -127 = %d, want -128", r) + } + y = -1 + r = x * y + if r != -128 { + t.Errorf("-128 * -1 = %d, want -128", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-128 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -128 { + t.Errorf("-128 * 1 = %d, want -128", r) + } + y = 126 + r = x * y + if r != 0 { + t.Errorf("-128 * 126 = %d, want 0", r) + } + y = 127 + r = x * y + if r != -128 { + t.Errorf("-128 * 127 = %d, want -128", r) + } + x = -127 + y = -128 + r = x * y + if r != -128 { + t.Errorf("-127 * -128 = %d, want -128", r) + } + y = -127 + r = x * y + if r != 1 { + t.Errorf("-127 * -127 = %d, want 1", r) + } + y = -1 + r = x * y + if r != 127 { + t.Errorf("-127 * -1 = %d, want 127", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-127 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -127 { + t.Errorf("-127 * 1 = %d, want -127", r) + } + y = 126 + r = x * y + if r != 126 { + t.Errorf("-127 * 126 = %d, want 126", r) + } + y = 127 + r = x * y + if r != -1 { + t.Errorf("-127 * 127 = %d, want -1", r) + } + x = -1 + y = -128 + r = x * y + if r != -128 { + t.Errorf("-1 * -128 = %d, want -128", r) + } + y = -127 + r = x * y + if r != 127 { + t.Errorf("-1 * -127 = %d, want 127", r) + } + y = -1 + r = x * y + if r != 1 { + t.Errorf("-1 * -1 = %d, want 1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("-1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != -1 { + t.Errorf("-1 * 1 = %d, want -1", r) + } + y = 126 + r = x * y + if r != -126 { + t.Errorf("-1 * 126 = %d, want -126", r) + } + y = 127 + r = x * y + if r != -127 { + t.Errorf("-1 * 127 = %d, want -127", r) + } + x = 0 + y = -128 + r = x * y + if r != 0 { + t.Errorf("0 * -128 = %d, want 0", r) + } + y = -127 + r = x * y + if r != 0 { + t.Errorf("0 * -127 = %d, want 0", r) + } + y = -1 + r = x * y + if r != 0 { + t.Errorf("0 * -1 = %d, want 0", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("0 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 0 { + t.Errorf("0 * 1 = %d, want 0", r) + } + y = 126 + r = x * y + if r != 0 { + t.Errorf("0 * 126 = %d, want 0", r) + } + y = 127 + r = x * y + if r != 0 { + t.Errorf("0 * 127 = %d, want 0", r) + } + x = 1 + y = -128 + r = x * y + if r != -128 { + t.Errorf("1 * -128 = %d, want -128", r) + } + y = -127 + r = x * y + if r != -127 { + t.Errorf("1 * -127 = %d, want -127", r) + } + y = -1 + r = x * y + if r != -1 { + t.Errorf("1 * -1 = %d, want -1", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("1 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 1 { + t.Errorf("1 * 1 = %d, want 1", r) + } + y = 126 + r = x * y + if r != 126 { + t.Errorf("1 * 126 = %d, want 126", r) + } + y = 127 + r = x * y + if r != 127 { + t.Errorf("1 * 127 = %d, want 127", r) + } + x = 126 + y = -128 + r = x * y + if r != 0 { + t.Errorf("126 * -128 = %d, want 0", r) + } + y = -127 + r = x * y + if r != 126 { + t.Errorf("126 * -127 = %d, want 126", r) + } + y = -1 + r = x * y + if r != -126 { + t.Errorf("126 * -1 = %d, want -126", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("126 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 126 { + t.Errorf("126 * 1 = %d, want 126", r) + } + y = 126 + r = x * y + if r != 4 { + t.Errorf("126 * 126 = %d, want 4", r) + } + y = 127 + r = x * y + if r != -126 { + t.Errorf("126 * 127 = %d, want -126", r) + } + x = 127 + y = -128 + r = x * y + if r != -128 { + t.Errorf("127 * -128 = %d, want -128", r) + } + y = -127 + r = x * y + if r != -1 { + t.Errorf("127 * -127 = %d, want -1", r) + } + y = -1 + r = x * y + if r != -127 { + t.Errorf("127 * -1 = %d, want -127", r) + } + y = 0 + r = x * y + if r != 0 { + t.Errorf("127 * 0 = %d, want 0", r) + } + y = 1 + r = x * y + if r != 127 { + t.Errorf("127 * 1 = %d, want 127", r) + } + y = 126 + r = x * y + if r != -126 { + t.Errorf("127 * 126 = %d, want -126", r) + } + y = 127 + r = x * y + if r != 1 { + t.Errorf("127 * 127 = %d, want 1", r) + } +} +func TestConstFoldint8mod(t *testing.T) { + var x, y, r int8 + x = -128 + y = -128 + r = x % y + if r != 0 { + t.Errorf("-128 % -128 = %d, want 0", r) + } + y = -127 + r = x % y + if r != -1 { + t.Errorf("-128 % -127 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-128 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-128 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != -2 { + t.Errorf("-128 % 126 = %d, want -2", r) + } + y = 127 + r = x % y + if r != -1 { + t.Errorf("-128 % 127 = %d, want -1", r) + } + x = -127 + y = -128 + r = x % y + if r != -127 { + t.Errorf("-127 % -128 = %d, want -127", r) + } + y = -127 + r = x % y + if r != 0 { + t.Errorf("-127 % -127 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-127 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-127 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != -1 { + t.Errorf("-127 % 126 = %d, want -1", r) + } + y = 127 + r = x % y + if r != 0 { + t.Errorf("-127 % 127 = %d, want 0", r) + } + x = -1 + y = -128 + r = x % y + if r != -1 { + t.Errorf("-1 % -128 = %d, want -1", r) + } + y = -127 + r = x % y + if r != -1 { + t.Errorf("-1 % -127 = %d, want -1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("-1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("-1 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != -1 { + t.Errorf("-1 % 126 = %d, want -1", r) + } + y = 127 + r = x % y + if r != -1 { + t.Errorf("-1 % 127 = %d, want -1", r) + } + x = 0 + y = -128 + r = x % y + if r != 0 { + t.Errorf("0 % -128 = %d, want 0", r) + } + y = -127 + r = x % y + if r != 0 { + t.Errorf("0 % -127 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("0 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("0 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != 0 { + t.Errorf("0 % 126 = %d, want 0", r) + } + y = 127 + r = x % y + if r != 0 { + t.Errorf("0 % 127 = %d, want 0", r) + } + x = 1 + y = -128 + r = x % y + if r != 1 { + t.Errorf("1 % -128 = %d, want 1", r) + } + y = -127 + r = x % y + if r != 1 { + t.Errorf("1 % -127 = %d, want 1", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("1 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("1 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != 1 { + t.Errorf("1 % 126 = %d, want 1", r) + } + y = 127 + r = x % y + if r != 1 { + t.Errorf("1 % 127 = %d, want 1", r) + } + x = 126 + y = -128 + r = x % y + if r != 126 { + t.Errorf("126 % -128 = %d, want 126", r) + } + y = -127 + r = x % y + if r != 126 { + t.Errorf("126 % -127 = %d, want 126", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("126 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("126 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != 0 { + t.Errorf("126 % 126 = %d, want 0", r) + } + y = 127 + r = x % y + if r != 126 { + t.Errorf("126 % 127 = %d, want 126", r) + } + x = 127 + y = -128 + r = x % y + if r != 127 { + t.Errorf("127 % -128 = %d, want 127", r) + } + y = -127 + r = x % y + if r != 0 { + t.Errorf("127 % -127 = %d, want 0", r) + } + y = -1 + r = x % y + if r != 0 { + t.Errorf("127 % -1 = %d, want 0", r) + } + y = 1 + r = x % y + if r != 0 { + t.Errorf("127 % 1 = %d, want 0", r) + } + y = 126 + r = x % y + if r != 1 { + t.Errorf("127 % 126 = %d, want 1", r) + } + y = 127 + r = x % y + if r != 0 { + t.Errorf("127 % 127 = %d, want 0", r) + } +} +func TestConstFolduint64uint64lsh(t *testing.T) { + var x, r uint64 + var y uint64 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 18446744073709551615 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x << y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x << y + if r != 18446744073709551614 { + t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("18446744073709551615 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("18446744073709551615 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint64uint64rsh(t *testing.T) { + var x, r uint64 + var y uint64 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 18446744073709551615 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x >> y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("18446744073709551615 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("18446744073709551615 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint64uint32lsh(t *testing.T) { + var x, r uint64 + var y uint32 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 4294967295 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x << y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x << y + if r != 18446744073709551614 { + t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("18446744073709551615 << 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint64uint32rsh(t *testing.T) { + var x, r uint64 + var y uint32 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 4294967295 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x >> y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("18446744073709551615 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint64uint16lsh(t *testing.T) { + var x, r uint64 + var y uint16 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 65535 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x << y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x << y + if r != 18446744073709551614 { + t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("18446744073709551615 << 65535 = %d, want 0", r) + } +} +func TestConstFolduint64uint16rsh(t *testing.T) { + var x, r uint64 + var y uint16 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 65535 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x >> y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("18446744073709551615 >> 65535 = %d, want 0", r) + } +} +func TestConstFolduint64uint8lsh(t *testing.T) { + var x, r uint64 + var y uint8 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 255 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x << y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x << y + if r != 18446744073709551614 { + t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("18446744073709551615 << 255 = %d, want 0", r) + } +} +func TestConstFolduint64uint8rsh(t *testing.T) { + var x, r uint64 + var y uint8 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 255 = %d, want 0", r) + } + x = 18446744073709551615 + y = 0 + r = x >> y + if r != 18446744073709551615 { + t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r) + } + y = 1 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("18446744073709551615 >> 255 = %d, want 0", r) + } +} +func TestConstFoldint64uint64lsh(t *testing.T) { + var x, r int64 + var y uint64 + x = -9223372036854775808 + y = 0 + r = x << y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 18446744073709551615 = %d, want 0", r) + } + x = -9223372036854775807 + y = 0 + r = x << y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-9223372036854775807 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775807 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775807 << 18446744073709551615 = %d, want 0", r) + } + x = -4294967296 + y = 0 + r = x << y + if r != -4294967296 { + t.Errorf("-4294967296 << 0 = %d, want -4294967296", r) + } + y = 1 + r = x << y + if r != -8589934592 { + t.Errorf("-4294967296 << 1 = %d, want -8589934592", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-4294967296 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-4294967296 << 18446744073709551615 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-1 << 18446744073709551615 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 18446744073709551615 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x << y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("9223372036854775806 << 1 = %d, want -4", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("9223372036854775806 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("9223372036854775806 << 18446744073709551615 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x << y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("9223372036854775807 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("9223372036854775807 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("9223372036854775807 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint64uint64rsh(t *testing.T) { + var x, r int64 + var y uint64 + x = -9223372036854775808 + y = 0 + r = x >> y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775808 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775808 >> 18446744073709551615 = %d, want -1", r) + } + x = -9223372036854775807 + y = 0 + r = x >> y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775807 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775807 >> 18446744073709551615 = %d, want -1", r) + } + x = -4294967296 + y = 0 + r = x >> y + if r != -4294967296 { + t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r) + } + y = 1 + r = x >> y + if r != -2147483648 { + t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-4294967296 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-4294967296 >> 18446744073709551615 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 18446744073709551615 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x >> y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775806 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775806 >> 18446744073709551615 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775807 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775807 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint64uint32lsh(t *testing.T) { + var x, r int64 + var y uint32 + x = -9223372036854775808 + y = 0 + r = x << y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 4294967295 = %d, want 0", r) + } + x = -9223372036854775807 + y = 0 + r = x << y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-9223372036854775807 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775807 << 4294967295 = %d, want 0", r) + } + x = -4294967296 + y = 0 + r = x << y + if r != -4294967296 { + t.Errorf("-4294967296 << 0 = %d, want -4294967296", r) + } + y = 1 + r = x << y + if r != -8589934592 { + t.Errorf("-4294967296 << 1 = %d, want -8589934592", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-4294967296 << 4294967295 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967295 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 4294967295 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x << y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("9223372036854775806 << 1 = %d, want -4", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("9223372036854775806 << 4294967295 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x << y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("9223372036854775807 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("9223372036854775807 << 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint64uint32rsh(t *testing.T) { + var x, r int64 + var y uint32 + x = -9223372036854775808 + y = 0 + r = x >> y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775808 >> 4294967295 = %d, want -1", r) + } + x = -9223372036854775807 + y = 0 + r = x >> y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775807 >> 4294967295 = %d, want -1", r) + } + x = -4294967296 + y = 0 + r = x >> y + if r != -4294967296 { + t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r) + } + y = 1 + r = x >> y + if r != -2147483648 { + t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-4294967296 >> 4294967295 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967295 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 4294967295 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x >> y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775806 >> 4294967295 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775807 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint64uint16lsh(t *testing.T) { + var x, r int64 + var y uint16 + x = -9223372036854775808 + y = 0 + r = x << y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 65535 = %d, want 0", r) + } + x = -9223372036854775807 + y = 0 + r = x << y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-9223372036854775807 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775807 << 65535 = %d, want 0", r) + } + x = -4294967296 + y = 0 + r = x << y + if r != -4294967296 { + t.Errorf("-4294967296 << 0 = %d, want -4294967296", r) + } + y = 1 + r = x << y + if r != -8589934592 { + t.Errorf("-4294967296 << 1 = %d, want -8589934592", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-4294967296 << 65535 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-1 << 65535 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 65535 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x << y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("9223372036854775806 << 1 = %d, want -4", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("9223372036854775806 << 65535 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x << y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("9223372036854775807 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("9223372036854775807 << 65535 = %d, want 0", r) + } +} +func TestConstFoldint64uint16rsh(t *testing.T) { + var x, r int64 + var y uint16 + x = -9223372036854775808 + y = 0 + r = x >> y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775808 >> 65535 = %d, want -1", r) + } + x = -9223372036854775807 + y = 0 + r = x >> y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775807 >> 65535 = %d, want -1", r) + } + x = -4294967296 + y = 0 + r = x >> y + if r != -4294967296 { + t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r) + } + y = 1 + r = x >> y + if r != -2147483648 { + t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-4294967296 >> 65535 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 65535 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 65535 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x >> y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775806 >> 65535 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775807 >> 65535 = %d, want 0", r) + } +} +func TestConstFoldint64uint8lsh(t *testing.T) { + var x, r int64 + var y uint8 + x = -9223372036854775808 + y = 0 + r = x << y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775808 << 255 = %d, want 0", r) + } + x = -9223372036854775807 + y = 0 + r = x << y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-9223372036854775807 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-9223372036854775807 << 255 = %d, want 0", r) + } + x = -4294967296 + y = 0 + r = x << y + if r != -4294967296 { + t.Errorf("-4294967296 << 0 = %d, want -4294967296", r) + } + y = 1 + r = x << y + if r != -8589934592 { + t.Errorf("-4294967296 << 1 = %d, want -8589934592", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-4294967296 << 255 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-1 << 255 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x << y + if r != 4294967296 { + t.Errorf("4294967296 << 0 = %d, want 4294967296", r) + } + y = 1 + r = x << y + if r != 8589934592 { + t.Errorf("4294967296 << 1 = %d, want 8589934592", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("4294967296 << 255 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x << y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("9223372036854775806 << 1 = %d, want -4", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("9223372036854775806 << 255 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x << y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("9223372036854775807 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("9223372036854775807 << 255 = %d, want 0", r) + } +} +func TestConstFoldint64uint8rsh(t *testing.T) { + var x, r int64 + var y uint8 + x = -9223372036854775808 + y = 0 + r = x >> y + if r != -9223372036854775808 { + t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775808 >> 255 = %d, want -1", r) + } + x = -9223372036854775807 + y = 0 + r = x >> y + if r != -9223372036854775807 { + t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r) + } + y = 1 + r = x >> y + if r != -4611686018427387904 { + t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-9223372036854775807 >> 255 = %d, want -1", r) + } + x = -4294967296 + y = 0 + r = x >> y + if r != -4294967296 { + t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r) + } + y = 1 + r = x >> y + if r != -2147483648 { + t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-4294967296 >> 255 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 255 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 4294967296 + y = 0 + r = x >> y + if r != 4294967296 { + t.Errorf("4294967296 >> 0 = %d, want 4294967296", r) + } + y = 1 + r = x >> y + if r != 2147483648 { + t.Errorf("4294967296 >> 1 = %d, want 2147483648", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("4294967296 >> 255 = %d, want 0", r) + } + x = 9223372036854775806 + y = 0 + r = x >> y + if r != 9223372036854775806 { + t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775806 >> 255 = %d, want 0", r) + } + x = 9223372036854775807 + y = 0 + r = x >> y + if r != 9223372036854775807 { + t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r) + } + y = 1 + r = x >> y + if r != 4611686018427387903 { + t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("9223372036854775807 >> 255 = %d, want 0", r) + } +} +func TestConstFolduint32uint64lsh(t *testing.T) { + var x, r uint32 + var y uint64 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x << y + if r != 4294967295 { + t.Errorf("4294967295 << 0 = %d, want 4294967295", r) + } + y = 1 + r = x << y + if r != 4294967294 { + t.Errorf("4294967295 << 1 = %d, want 4294967294", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("4294967295 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("4294967295 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint32uint64rsh(t *testing.T) { + var x, r uint32 + var y uint64 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x >> y + if r != 4294967295 { + t.Errorf("4294967295 >> 0 = %d, want 4294967295", r) + } + y = 1 + r = x >> y + if r != 2147483647 { + t.Errorf("4294967295 >> 1 = %d, want 2147483647", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("4294967295 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("4294967295 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint32uint32lsh(t *testing.T) { + var x, r uint32 + var y uint32 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x << y + if r != 4294967295 { + t.Errorf("4294967295 << 0 = %d, want 4294967295", r) + } + y = 1 + r = x << y + if r != 4294967294 { + t.Errorf("4294967295 << 1 = %d, want 4294967294", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("4294967295 << 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint32uint32rsh(t *testing.T) { + var x, r uint32 + var y uint32 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x >> y + if r != 4294967295 { + t.Errorf("4294967295 >> 0 = %d, want 4294967295", r) + } + y = 1 + r = x >> y + if r != 2147483647 { + t.Errorf("4294967295 >> 1 = %d, want 2147483647", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("4294967295 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint32uint16lsh(t *testing.T) { + var x, r uint32 + var y uint16 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x << y + if r != 4294967295 { + t.Errorf("4294967295 << 0 = %d, want 4294967295", r) + } + y = 1 + r = x << y + if r != 4294967294 { + t.Errorf("4294967295 << 1 = %d, want 4294967294", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("4294967295 << 65535 = %d, want 0", r) + } +} +func TestConstFolduint32uint16rsh(t *testing.T) { + var x, r uint32 + var y uint16 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x >> y + if r != 4294967295 { + t.Errorf("4294967295 >> 0 = %d, want 4294967295", r) + } + y = 1 + r = x >> y + if r != 2147483647 { + t.Errorf("4294967295 >> 1 = %d, want 2147483647", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("4294967295 >> 65535 = %d, want 0", r) + } +} +func TestConstFolduint32uint8lsh(t *testing.T) { + var x, r uint32 + var y uint8 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x << y + if r != 4294967295 { + t.Errorf("4294967295 << 0 = %d, want 4294967295", r) + } + y = 1 + r = x << y + if r != 4294967294 { + t.Errorf("4294967295 << 1 = %d, want 4294967294", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("4294967295 << 255 = %d, want 0", r) + } +} +func TestConstFolduint32uint8rsh(t *testing.T) { + var x, r uint32 + var y uint8 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 4294967295 + y = 0 + r = x >> y + if r != 4294967295 { + t.Errorf("4294967295 >> 0 = %d, want 4294967295", r) + } + y = 1 + r = x >> y + if r != 2147483647 { + t.Errorf("4294967295 >> 1 = %d, want 2147483647", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("4294967295 >> 255 = %d, want 0", r) + } +} +func TestConstFoldint32uint64lsh(t *testing.T) { + var x, r int32 + var y uint64 + x = -2147483648 + y = 0 + r = x << y + if r != -2147483648 { + t.Errorf("-2147483648 << 0 = %d, want -2147483648", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 18446744073709551615 = %d, want 0", r) + } + x = -2147483647 + y = 0 + r = x << y + if r != -2147483647 { + t.Errorf("-2147483647 << 0 = %d, want -2147483647", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-2147483647 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-2147483647 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-2147483647 << 18446744073709551615 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-1 << 18446744073709551615 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x << y + if r != 2147483647 { + t.Errorf("2147483647 << 0 = %d, want 2147483647", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("2147483647 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("2147483647 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("2147483647 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint32uint64rsh(t *testing.T) { + var x, r int32 + var y uint64 + x = -2147483648 + y = 0 + r = x >> y + if r != -2147483648 { + t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-2147483648 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-2147483648 >> 18446744073709551615 = %d, want -1", r) + } + x = -2147483647 + y = 0 + r = x >> y + if r != -2147483647 { + t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-2147483647 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-2147483647 >> 18446744073709551615 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x >> y + if r != 2147483647 { + t.Errorf("2147483647 >> 0 = %d, want 2147483647", r) + } + y = 1 + r = x >> y + if r != 1073741823 { + t.Errorf("2147483647 >> 1 = %d, want 1073741823", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("2147483647 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("2147483647 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint32uint32lsh(t *testing.T) { + var x, r int32 + var y uint32 + x = -2147483648 + y = 0 + r = x << y + if r != -2147483648 { + t.Errorf("-2147483648 << 0 = %d, want -2147483648", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 4294967295 = %d, want 0", r) + } + x = -2147483647 + y = 0 + r = x << y + if r != -2147483647 { + t.Errorf("-2147483647 << 0 = %d, want -2147483647", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-2147483647 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-2147483647 << 4294967295 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967295 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x << y + if r != 2147483647 { + t.Errorf("2147483647 << 0 = %d, want 2147483647", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("2147483647 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("2147483647 << 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint32uint32rsh(t *testing.T) { + var x, r int32 + var y uint32 + x = -2147483648 + y = 0 + r = x >> y + if r != -2147483648 { + t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-2147483648 >> 4294967295 = %d, want -1", r) + } + x = -2147483647 + y = 0 + r = x >> y + if r != -2147483647 { + t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-2147483647 >> 4294967295 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967295 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x >> y + if r != 2147483647 { + t.Errorf("2147483647 >> 0 = %d, want 2147483647", r) + } + y = 1 + r = x >> y + if r != 1073741823 { + t.Errorf("2147483647 >> 1 = %d, want 1073741823", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("2147483647 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint32uint16lsh(t *testing.T) { + var x, r int32 + var y uint16 + x = -2147483648 + y = 0 + r = x << y + if r != -2147483648 { + t.Errorf("-2147483648 << 0 = %d, want -2147483648", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 65535 = %d, want 0", r) + } + x = -2147483647 + y = 0 + r = x << y + if r != -2147483647 { + t.Errorf("-2147483647 << 0 = %d, want -2147483647", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-2147483647 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-2147483647 << 65535 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-1 << 65535 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x << y + if r != 2147483647 { + t.Errorf("2147483647 << 0 = %d, want 2147483647", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("2147483647 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("2147483647 << 65535 = %d, want 0", r) + } +} +func TestConstFoldint32uint16rsh(t *testing.T) { + var x, r int32 + var y uint16 + x = -2147483648 + y = 0 + r = x >> y + if r != -2147483648 { + t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-2147483648 >> 65535 = %d, want -1", r) + } + x = -2147483647 + y = 0 + r = x >> y + if r != -2147483647 { + t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-2147483647 >> 65535 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 65535 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x >> y + if r != 2147483647 { + t.Errorf("2147483647 >> 0 = %d, want 2147483647", r) + } + y = 1 + r = x >> y + if r != 1073741823 { + t.Errorf("2147483647 >> 1 = %d, want 1073741823", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("2147483647 >> 65535 = %d, want 0", r) + } +} +func TestConstFoldint32uint8lsh(t *testing.T) { + var x, r int32 + var y uint8 + x = -2147483648 + y = 0 + r = x << y + if r != -2147483648 { + t.Errorf("-2147483648 << 0 = %d, want -2147483648", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-2147483648 << 255 = %d, want 0", r) + } + x = -2147483647 + y = 0 + r = x << y + if r != -2147483647 { + t.Errorf("-2147483647 << 0 = %d, want -2147483647", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-2147483647 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-2147483647 << 255 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-1 << 255 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x << y + if r != 2147483647 { + t.Errorf("2147483647 << 0 = %d, want 2147483647", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("2147483647 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("2147483647 << 255 = %d, want 0", r) + } +} +func TestConstFoldint32uint8rsh(t *testing.T) { + var x, r int32 + var y uint8 + x = -2147483648 + y = 0 + r = x >> y + if r != -2147483648 { + t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-2147483648 >> 255 = %d, want -1", r) + } + x = -2147483647 + y = 0 + r = x >> y + if r != -2147483647 { + t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r) + } + y = 1 + r = x >> y + if r != -1073741824 { + t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-2147483647 >> 255 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 255 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 2147483647 + y = 0 + r = x >> y + if r != 2147483647 { + t.Errorf("2147483647 >> 0 = %d, want 2147483647", r) + } + y = 1 + r = x >> y + if r != 1073741823 { + t.Errorf("2147483647 >> 1 = %d, want 1073741823", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("2147483647 >> 255 = %d, want 0", r) + } +} +func TestConstFolduint16uint64lsh(t *testing.T) { + var x, r uint16 + var y uint64 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x << y + if r != 65535 { + t.Errorf("65535 << 0 = %d, want 65535", r) + } + y = 1 + r = x << y + if r != 65534 { + t.Errorf("65535 << 1 = %d, want 65534", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("65535 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("65535 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint16uint64rsh(t *testing.T) { + var x, r uint16 + var y uint64 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x >> y + if r != 65535 { + t.Errorf("65535 >> 0 = %d, want 65535", r) + } + y = 1 + r = x >> y + if r != 32767 { + t.Errorf("65535 >> 1 = %d, want 32767", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("65535 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("65535 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint16uint32lsh(t *testing.T) { + var x, r uint16 + var y uint32 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x << y + if r != 65535 { + t.Errorf("65535 << 0 = %d, want 65535", r) + } + y = 1 + r = x << y + if r != 65534 { + t.Errorf("65535 << 1 = %d, want 65534", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("65535 << 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint16uint32rsh(t *testing.T) { + var x, r uint16 + var y uint32 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x >> y + if r != 65535 { + t.Errorf("65535 >> 0 = %d, want 65535", r) + } + y = 1 + r = x >> y + if r != 32767 { + t.Errorf("65535 >> 1 = %d, want 32767", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("65535 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint16uint16lsh(t *testing.T) { + var x, r uint16 + var y uint16 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x << y + if r != 65535 { + t.Errorf("65535 << 0 = %d, want 65535", r) + } + y = 1 + r = x << y + if r != 65534 { + t.Errorf("65535 << 1 = %d, want 65534", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("65535 << 65535 = %d, want 0", r) + } +} +func TestConstFolduint16uint16rsh(t *testing.T) { + var x, r uint16 + var y uint16 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x >> y + if r != 65535 { + t.Errorf("65535 >> 0 = %d, want 65535", r) + } + y = 1 + r = x >> y + if r != 32767 { + t.Errorf("65535 >> 1 = %d, want 32767", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("65535 >> 65535 = %d, want 0", r) + } +} +func TestConstFolduint16uint8lsh(t *testing.T) { + var x, r uint16 + var y uint8 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x << y + if r != 65535 { + t.Errorf("65535 << 0 = %d, want 65535", r) + } + y = 1 + r = x << y + if r != 65534 { + t.Errorf("65535 << 1 = %d, want 65534", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("65535 << 255 = %d, want 0", r) + } +} +func TestConstFolduint16uint8rsh(t *testing.T) { + var x, r uint16 + var y uint8 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 65535 + y = 0 + r = x >> y + if r != 65535 { + t.Errorf("65535 >> 0 = %d, want 65535", r) + } + y = 1 + r = x >> y + if r != 32767 { + t.Errorf("65535 >> 1 = %d, want 32767", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("65535 >> 255 = %d, want 0", r) + } +} +func TestConstFoldint16uint64lsh(t *testing.T) { + var x, r int16 + var y uint64 + x = -32768 + y = 0 + r = x << y + if r != -32768 { + t.Errorf("-32768 << 0 = %d, want -32768", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-32768 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-32768 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-32768 << 18446744073709551615 = %d, want 0", r) + } + x = -32767 + y = 0 + r = x << y + if r != -32767 { + t.Errorf("-32767 << 0 = %d, want -32767", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-32767 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-32767 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-32767 << 18446744073709551615 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-1 << 18446744073709551615 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x << y + if r != 32766 { + t.Errorf("32766 << 0 = %d, want 32766", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("32766 << 1 = %d, want -4", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("32766 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("32766 << 18446744073709551615 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x << y + if r != 32767 { + t.Errorf("32767 << 0 = %d, want 32767", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("32767 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("32767 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("32767 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint16uint64rsh(t *testing.T) { + var x, r int16 + var y uint64 + x = -32768 + y = 0 + r = x >> y + if r != -32768 { + t.Errorf("-32768 >> 0 = %d, want -32768", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32768 >> 1 = %d, want -16384", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-32768 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-32768 >> 18446744073709551615 = %d, want -1", r) + } + x = -32767 + y = 0 + r = x >> y + if r != -32767 { + t.Errorf("-32767 >> 0 = %d, want -32767", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32767 >> 1 = %d, want -16384", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-32767 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-32767 >> 18446744073709551615 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x >> y + if r != 32766 { + t.Errorf("32766 >> 0 = %d, want 32766", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32766 >> 1 = %d, want 16383", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("32766 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("32766 >> 18446744073709551615 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x >> y + if r != 32767 { + t.Errorf("32767 >> 0 = %d, want 32767", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32767 >> 1 = %d, want 16383", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("32767 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("32767 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint16uint32lsh(t *testing.T) { + var x, r int16 + var y uint32 + x = -32768 + y = 0 + r = x << y + if r != -32768 { + t.Errorf("-32768 << 0 = %d, want -32768", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-32768 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-32768 << 4294967295 = %d, want 0", r) + } + x = -32767 + y = 0 + r = x << y + if r != -32767 { + t.Errorf("-32767 << 0 = %d, want -32767", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-32767 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-32767 << 4294967295 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967295 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x << y + if r != 32766 { + t.Errorf("32766 << 0 = %d, want 32766", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("32766 << 1 = %d, want -4", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("32766 << 4294967295 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x << y + if r != 32767 { + t.Errorf("32767 << 0 = %d, want 32767", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("32767 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("32767 << 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint16uint32rsh(t *testing.T) { + var x, r int16 + var y uint32 + x = -32768 + y = 0 + r = x >> y + if r != -32768 { + t.Errorf("-32768 >> 0 = %d, want -32768", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32768 >> 1 = %d, want -16384", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-32768 >> 4294967295 = %d, want -1", r) + } + x = -32767 + y = 0 + r = x >> y + if r != -32767 { + t.Errorf("-32767 >> 0 = %d, want -32767", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32767 >> 1 = %d, want -16384", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-32767 >> 4294967295 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967295 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x >> y + if r != 32766 { + t.Errorf("32766 >> 0 = %d, want 32766", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32766 >> 1 = %d, want 16383", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("32766 >> 4294967295 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x >> y + if r != 32767 { + t.Errorf("32767 >> 0 = %d, want 32767", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32767 >> 1 = %d, want 16383", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("32767 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint16uint16lsh(t *testing.T) { + var x, r int16 + var y uint16 + x = -32768 + y = 0 + r = x << y + if r != -32768 { + t.Errorf("-32768 << 0 = %d, want -32768", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-32768 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-32768 << 65535 = %d, want 0", r) + } + x = -32767 + y = 0 + r = x << y + if r != -32767 { + t.Errorf("-32767 << 0 = %d, want -32767", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-32767 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-32767 << 65535 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-1 << 65535 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x << y + if r != 32766 { + t.Errorf("32766 << 0 = %d, want 32766", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("32766 << 1 = %d, want -4", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("32766 << 65535 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x << y + if r != 32767 { + t.Errorf("32767 << 0 = %d, want 32767", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("32767 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("32767 << 65535 = %d, want 0", r) + } +} +func TestConstFoldint16uint16rsh(t *testing.T) { + var x, r int16 + var y uint16 + x = -32768 + y = 0 + r = x >> y + if r != -32768 { + t.Errorf("-32768 >> 0 = %d, want -32768", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32768 >> 1 = %d, want -16384", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-32768 >> 65535 = %d, want -1", r) + } + x = -32767 + y = 0 + r = x >> y + if r != -32767 { + t.Errorf("-32767 >> 0 = %d, want -32767", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32767 >> 1 = %d, want -16384", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-32767 >> 65535 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 65535 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x >> y + if r != 32766 { + t.Errorf("32766 >> 0 = %d, want 32766", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32766 >> 1 = %d, want 16383", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("32766 >> 65535 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x >> y + if r != 32767 { + t.Errorf("32767 >> 0 = %d, want 32767", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32767 >> 1 = %d, want 16383", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("32767 >> 65535 = %d, want 0", r) + } +} +func TestConstFoldint16uint8lsh(t *testing.T) { + var x, r int16 + var y uint8 + x = -32768 + y = 0 + r = x << y + if r != -32768 { + t.Errorf("-32768 << 0 = %d, want -32768", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-32768 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-32768 << 255 = %d, want 0", r) + } + x = -32767 + y = 0 + r = x << y + if r != -32767 { + t.Errorf("-32767 << 0 = %d, want -32767", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-32767 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-32767 << 255 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-1 << 255 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x << y + if r != 32766 { + t.Errorf("32766 << 0 = %d, want 32766", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("32766 << 1 = %d, want -4", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("32766 << 255 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x << y + if r != 32767 { + t.Errorf("32767 << 0 = %d, want 32767", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("32767 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("32767 << 255 = %d, want 0", r) + } +} +func TestConstFoldint16uint8rsh(t *testing.T) { + var x, r int16 + var y uint8 + x = -32768 + y = 0 + r = x >> y + if r != -32768 { + t.Errorf("-32768 >> 0 = %d, want -32768", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32768 >> 1 = %d, want -16384", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-32768 >> 255 = %d, want -1", r) + } + x = -32767 + y = 0 + r = x >> y + if r != -32767 { + t.Errorf("-32767 >> 0 = %d, want -32767", r) + } + y = 1 + r = x >> y + if r != -16384 { + t.Errorf("-32767 >> 1 = %d, want -16384", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-32767 >> 255 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 255 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 32766 + y = 0 + r = x >> y + if r != 32766 { + t.Errorf("32766 >> 0 = %d, want 32766", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32766 >> 1 = %d, want 16383", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("32766 >> 255 = %d, want 0", r) + } + x = 32767 + y = 0 + r = x >> y + if r != 32767 { + t.Errorf("32767 >> 0 = %d, want 32767", r) + } + y = 1 + r = x >> y + if r != 16383 { + t.Errorf("32767 >> 1 = %d, want 16383", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("32767 >> 255 = %d, want 0", r) + } +} +func TestConstFolduint8uint64lsh(t *testing.T) { + var x, r uint8 + var y uint64 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 255 + y = 0 + r = x << y + if r != 255 { + t.Errorf("255 << 0 = %d, want 255", r) + } + y = 1 + r = x << y + if r != 254 { + t.Errorf("255 << 1 = %d, want 254", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("255 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("255 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint8uint64rsh(t *testing.T) { + var x, r uint8 + var y uint64 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 255 + y = 0 + r = x >> y + if r != 255 { + t.Errorf("255 >> 0 = %d, want 255", r) + } + y = 1 + r = x >> y + if r != 127 { + t.Errorf("255 >> 1 = %d, want 127", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("255 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("255 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFolduint8uint32lsh(t *testing.T) { + var x, r uint8 + var y uint32 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 255 + y = 0 + r = x << y + if r != 255 { + t.Errorf("255 << 0 = %d, want 255", r) + } + y = 1 + r = x << y + if r != 254 { + t.Errorf("255 << 1 = %d, want 254", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("255 << 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint8uint32rsh(t *testing.T) { + var x, r uint8 + var y uint32 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 255 + y = 0 + r = x >> y + if r != 255 { + t.Errorf("255 >> 0 = %d, want 255", r) + } + y = 1 + r = x >> y + if r != 127 { + t.Errorf("255 >> 1 = %d, want 127", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("255 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFolduint8uint16lsh(t *testing.T) { + var x, r uint8 + var y uint16 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 255 + y = 0 + r = x << y + if r != 255 { + t.Errorf("255 << 0 = %d, want 255", r) + } + y = 1 + r = x << y + if r != 254 { + t.Errorf("255 << 1 = %d, want 254", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("255 << 65535 = %d, want 0", r) + } +} +func TestConstFolduint8uint16rsh(t *testing.T) { + var x, r uint8 + var y uint16 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 255 + y = 0 + r = x >> y + if r != 255 { + t.Errorf("255 >> 0 = %d, want 255", r) + } + y = 1 + r = x >> y + if r != 127 { + t.Errorf("255 >> 1 = %d, want 127", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("255 >> 65535 = %d, want 0", r) + } +} +func TestConstFolduint8uint8lsh(t *testing.T) { + var x, r uint8 + var y uint8 + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 255 + y = 0 + r = x << y + if r != 255 { + t.Errorf("255 << 0 = %d, want 255", r) + } + y = 1 + r = x << y + if r != 254 { + t.Errorf("255 << 1 = %d, want 254", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("255 << 255 = %d, want 0", r) + } +} +func TestConstFolduint8uint8rsh(t *testing.T) { + var x, r uint8 + var y uint8 + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 255 + y = 0 + r = x >> y + if r != 255 { + t.Errorf("255 >> 0 = %d, want 255", r) + } + y = 1 + r = x >> y + if r != 127 { + t.Errorf("255 >> 1 = %d, want 127", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("255 >> 255 = %d, want 0", r) + } +} +func TestConstFoldint8uint64lsh(t *testing.T) { + var x, r int8 + var y uint64 + x = -128 + y = 0 + r = x << y + if r != -128 { + t.Errorf("-128 << 0 = %d, want -128", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-128 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-128 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-128 << 18446744073709551615 = %d, want 0", r) + } + x = -127 + y = 0 + r = x << y + if r != -127 { + t.Errorf("-127 << 0 = %d, want -127", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-127 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-127 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-127 << 18446744073709551615 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("-1 << 18446744073709551615 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("0 << 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("1 << 18446744073709551615 = %d, want 0", r) + } + x = 126 + y = 0 + r = x << y + if r != 126 { + t.Errorf("126 << 0 = %d, want 126", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("126 << 1 = %d, want -4", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("126 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("126 << 18446744073709551615 = %d, want 0", r) + } + x = 127 + y = 0 + r = x << y + if r != 127 { + t.Errorf("127 << 0 = %d, want 127", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("127 << 1 = %d, want -2", r) + } + y = 4294967296 + r = x << y + if r != 0 { + t.Errorf("127 << 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x << y + if r != 0 { + t.Errorf("127 << 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint8uint64rsh(t *testing.T) { + var x, r int8 + var y uint64 + x = -128 + y = 0 + r = x >> y + if r != -128 { + t.Errorf("-128 >> 0 = %d, want -128", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-128 >> 1 = %d, want -64", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-128 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-128 >> 18446744073709551615 = %d, want -1", r) + } + x = -127 + y = 0 + r = x >> y + if r != -127 { + t.Errorf("-127 >> 0 = %d, want -127", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-127 >> 1 = %d, want -64", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-127 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-127 >> 18446744073709551615 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967296 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967296 = %d, want -1", r) + } + y = 18446744073709551615 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("0 >> 18446744073709551615 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("1 >> 18446744073709551615 = %d, want 0", r) + } + x = 126 + y = 0 + r = x >> y + if r != 126 { + t.Errorf("126 >> 0 = %d, want 126", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("126 >> 1 = %d, want 63", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("126 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("126 >> 18446744073709551615 = %d, want 0", r) + } + x = 127 + y = 0 + r = x >> y + if r != 127 { + t.Errorf("127 >> 0 = %d, want 127", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("127 >> 1 = %d, want 63", r) + } + y = 4294967296 + r = x >> y + if r != 0 { + t.Errorf("127 >> 4294967296 = %d, want 0", r) + } + y = 18446744073709551615 + r = x >> y + if r != 0 { + t.Errorf("127 >> 18446744073709551615 = %d, want 0", r) + } +} +func TestConstFoldint8uint32lsh(t *testing.T) { + var x, r int8 + var y uint32 + x = -128 + y = 0 + r = x << y + if r != -128 { + t.Errorf("-128 << 0 = %d, want -128", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-128 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-128 << 4294967295 = %d, want 0", r) + } + x = -127 + y = 0 + r = x << y + if r != -127 { + t.Errorf("-127 << 0 = %d, want -127", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-127 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-127 << 4294967295 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("-1 << 4294967295 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("0 << 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("1 << 4294967295 = %d, want 0", r) + } + x = 126 + y = 0 + r = x << y + if r != 126 { + t.Errorf("126 << 0 = %d, want 126", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("126 << 1 = %d, want -4", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("126 << 4294967295 = %d, want 0", r) + } + x = 127 + y = 0 + r = x << y + if r != 127 { + t.Errorf("127 << 0 = %d, want 127", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("127 << 1 = %d, want -2", r) + } + y = 4294967295 + r = x << y + if r != 0 { + t.Errorf("127 << 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint8uint32rsh(t *testing.T) { + var x, r int8 + var y uint32 + x = -128 + y = 0 + r = x >> y + if r != -128 { + t.Errorf("-128 >> 0 = %d, want -128", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-128 >> 1 = %d, want -64", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-128 >> 4294967295 = %d, want -1", r) + } + x = -127 + y = 0 + r = x >> y + if r != -127 { + t.Errorf("-127 >> 0 = %d, want -127", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-127 >> 1 = %d, want -64", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-127 >> 4294967295 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 4294967295 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 4294967295 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("0 >> 4294967295 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("1 >> 4294967295 = %d, want 0", r) + } + x = 126 + y = 0 + r = x >> y + if r != 126 { + t.Errorf("126 >> 0 = %d, want 126", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("126 >> 1 = %d, want 63", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("126 >> 4294967295 = %d, want 0", r) + } + x = 127 + y = 0 + r = x >> y + if r != 127 { + t.Errorf("127 >> 0 = %d, want 127", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("127 >> 1 = %d, want 63", r) + } + y = 4294967295 + r = x >> y + if r != 0 { + t.Errorf("127 >> 4294967295 = %d, want 0", r) + } +} +func TestConstFoldint8uint16lsh(t *testing.T) { + var x, r int8 + var y uint16 + x = -128 + y = 0 + r = x << y + if r != -128 { + t.Errorf("-128 << 0 = %d, want -128", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-128 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-128 << 65535 = %d, want 0", r) + } + x = -127 + y = 0 + r = x << y + if r != -127 { + t.Errorf("-127 << 0 = %d, want -127", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-127 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-127 << 65535 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("-1 << 65535 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("0 << 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("1 << 65535 = %d, want 0", r) + } + x = 126 + y = 0 + r = x << y + if r != 126 { + t.Errorf("126 << 0 = %d, want 126", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("126 << 1 = %d, want -4", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("126 << 65535 = %d, want 0", r) + } + x = 127 + y = 0 + r = x << y + if r != 127 { + t.Errorf("127 << 0 = %d, want 127", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("127 << 1 = %d, want -2", r) + } + y = 65535 + r = x << y + if r != 0 { + t.Errorf("127 << 65535 = %d, want 0", r) + } +} +func TestConstFoldint8uint16rsh(t *testing.T) { + var x, r int8 + var y uint16 + x = -128 + y = 0 + r = x >> y + if r != -128 { + t.Errorf("-128 >> 0 = %d, want -128", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-128 >> 1 = %d, want -64", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-128 >> 65535 = %d, want -1", r) + } + x = -127 + y = 0 + r = x >> y + if r != -127 { + t.Errorf("-127 >> 0 = %d, want -127", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-127 >> 1 = %d, want -64", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-127 >> 65535 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 65535 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 65535 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("0 >> 65535 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("1 >> 65535 = %d, want 0", r) + } + x = 126 + y = 0 + r = x >> y + if r != 126 { + t.Errorf("126 >> 0 = %d, want 126", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("126 >> 1 = %d, want 63", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("126 >> 65535 = %d, want 0", r) + } + x = 127 + y = 0 + r = x >> y + if r != 127 { + t.Errorf("127 >> 0 = %d, want 127", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("127 >> 1 = %d, want 63", r) + } + y = 65535 + r = x >> y + if r != 0 { + t.Errorf("127 >> 65535 = %d, want 0", r) + } +} +func TestConstFoldint8uint8lsh(t *testing.T) { + var x, r int8 + var y uint8 + x = -128 + y = 0 + r = x << y + if r != -128 { + t.Errorf("-128 << 0 = %d, want -128", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("-128 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-128 << 255 = %d, want 0", r) + } + x = -127 + y = 0 + r = x << y + if r != -127 { + t.Errorf("-127 << 0 = %d, want -127", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("-127 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-127 << 255 = %d, want 0", r) + } + x = -1 + y = 0 + r = x << y + if r != -1 { + t.Errorf("-1 << 0 = %d, want -1", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("-1 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("-1 << 255 = %d, want 0", r) + } + x = 0 + y = 0 + r = x << y + if r != 0 { + t.Errorf("0 << 0 = %d, want 0", r) + } + y = 1 + r = x << y + if r != 0 { + t.Errorf("0 << 1 = %d, want 0", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("0 << 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x << y + if r != 1 { + t.Errorf("1 << 0 = %d, want 1", r) + } + y = 1 + r = x << y + if r != 2 { + t.Errorf("1 << 1 = %d, want 2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("1 << 255 = %d, want 0", r) + } + x = 126 + y = 0 + r = x << y + if r != 126 { + t.Errorf("126 << 0 = %d, want 126", r) + } + y = 1 + r = x << y + if r != -4 { + t.Errorf("126 << 1 = %d, want -4", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("126 << 255 = %d, want 0", r) + } + x = 127 + y = 0 + r = x << y + if r != 127 { + t.Errorf("127 << 0 = %d, want 127", r) + } + y = 1 + r = x << y + if r != -2 { + t.Errorf("127 << 1 = %d, want -2", r) + } + y = 255 + r = x << y + if r != 0 { + t.Errorf("127 << 255 = %d, want 0", r) + } +} +func TestConstFoldint8uint8rsh(t *testing.T) { + var x, r int8 + var y uint8 + x = -128 + y = 0 + r = x >> y + if r != -128 { + t.Errorf("-128 >> 0 = %d, want -128", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-128 >> 1 = %d, want -64", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-128 >> 255 = %d, want -1", r) + } + x = -127 + y = 0 + r = x >> y + if r != -127 { + t.Errorf("-127 >> 0 = %d, want -127", r) + } + y = 1 + r = x >> y + if r != -64 { + t.Errorf("-127 >> 1 = %d, want -64", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-127 >> 255 = %d, want -1", r) + } + x = -1 + y = 0 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 0 = %d, want -1", r) + } + y = 1 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 1 = %d, want -1", r) + } + y = 255 + r = x >> y + if r != -1 { + t.Errorf("-1 >> 255 = %d, want -1", r) + } + x = 0 + y = 0 + r = x >> y + if r != 0 { + t.Errorf("0 >> 0 = %d, want 0", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("0 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("0 >> 255 = %d, want 0", r) + } + x = 1 + y = 0 + r = x >> y + if r != 1 { + t.Errorf("1 >> 0 = %d, want 1", r) + } + y = 1 + r = x >> y + if r != 0 { + t.Errorf("1 >> 1 = %d, want 0", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("1 >> 255 = %d, want 0", r) + } + x = 126 + y = 0 + r = x >> y + if r != 126 { + t.Errorf("126 >> 0 = %d, want 126", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("126 >> 1 = %d, want 63", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("126 >> 255 = %d, want 0", r) + } + x = 127 + y = 0 + r = x >> y + if r != 127 { + t.Errorf("127 >> 0 = %d, want 127", r) + } + y = 1 + r = x >> y + if r != 63 { + t.Errorf("127 >> 1 = %d, want 63", r) + } + y = 255 + r = x >> y + if r != 0 { + t.Errorf("127 >> 255 = %d, want 0", r) + } +} diff --git a/src/cmd/compile/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go index 9bb202752064f2..96a1dfb3c26abf 100644 --- a/src/cmd/compile/internal/gc/cplx.go +++ b/src/cmd/compile/internal/gc/cplx.go @@ -405,7 +405,6 @@ func Complexgen(n *Node, res *Node) { ODOTPTR, OINDEX, OIND, - ONAME, // PHEAP or PPARAMREF var OCALLFUNC, OCALLMETH, OCALLINTER: diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index ba5b6b689cab01..a4b98ec7c5219a 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -385,32 +385,36 @@ func oldname(s *Sym) *Node { } if Curfn != nil && n.Op == ONAME && n.Name.Funcdepth > 0 && n.Name.Funcdepth != Funcdepth { - // inner func is referring to var in outer func. + // Inner func is referring to var in outer func. // // TODO(rsc): If there is an outer variable x and we // are parsing x := 5 inside the closure, until we get to // the := it looks like a reference to the outer x so we'll // make x a closure variable unnecessarily. - if n.Name.Param.Closure == nil || n.Name.Param.Closure.Name.Funcdepth != Funcdepth { - // create new closure var. - c := Nod(ONAME, nil, nil) - + c := n.Name.Param.Innermost + if c == nil || c.Name.Funcdepth != Funcdepth { + // Do not have a closure var for the active closure yet; make one. + c = Nod(ONAME, nil, nil) c.Sym = s - c.Class = PPARAMREF + c.Class = PAUTOHEAP + c.setIsClosureVar(true) c.Isddd = n.Isddd c.Name.Defn = n c.Addable = false c.Ullman = 2 c.Name.Funcdepth = Funcdepth - c.Name.Param.Outer = n.Name.Param.Closure - n.Name.Param.Closure = c - c.Name.Param.Closure = n + + // Link into list of active closure variables. + // Popped from list in func closurebody. + c.Name.Param.Outer = n.Name.Param.Innermost + n.Name.Param.Innermost = c + c.Xoffset = 0 Curfn.Func.Cvars.Append(c) } // return ref to closure var, not original - return n.Name.Param.Closure + return c } return n @@ -504,10 +508,8 @@ func ifacedcl(n *Node) { n.Func = new(Func) n.Func.FCurfn = Curfn dclcontext = PPARAM - markdcl() - Funcdepth++ - n.Func.Outer = Curfn - Curfn = n + + funcstart(n) funcargs(n.Right) // funcbody is normally called after the parser has @@ -534,11 +536,7 @@ func funchdr(n *Node) { } dclcontext = PAUTO - markdcl() - Funcdepth++ - - n.Func.Outer = Curfn - Curfn = n + funcstart(n) if n.Func.Nname != nil { funcargs(n.Func.Nname.Name.Param.Ntype) @@ -671,6 +669,18 @@ func funcargs2(t *Type) { } } +var funcstack []*Node // stack of previous values of Curfn +var Funcdepth int32 // len(funcstack) during parsing, but then forced to be the same later during compilation + +// start the function. +// called before funcargs; undone at end of funcbody. +func funcstart(n *Node) { + markdcl() + funcstack = append(funcstack, Curfn) + Funcdepth++ + Curfn = n +} + // finish the body. // called in auto-declaration context. // returns in extern-declaration context. @@ -680,9 +690,8 @@ func funcbody(n *Node) { Fatalf("funcbody: unexpected dclcontext %d", dclcontext) } popdcl() + funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1] Funcdepth-- - Curfn = n.Func.Outer - n.Func.Outer = nil if Funcdepth == 0 { dclcontext = PEXTERN } @@ -827,14 +836,14 @@ func tostruct0(t *Type, l []*Node) { } } -func tofunargs(l []*Node) *Type { +func tofunargs(l []*Node, funarg Funarg) *Type { t := typ(TSTRUCT) - t.StructType().Funarg = true + t.StructType().Funarg = funarg fields := make([]*Field, len(l)) for i, n := range l { f := structfield(n) - f.Funarg = true + f.Funarg = funarg // esc.go needs to find f given a PPARAM to add the tag. if n.Left != nil && n.Left.Class == PPARAM { @@ -1025,9 +1034,9 @@ func functype0(t *Type, this *Node, in, out []*Node) { if this != nil { rcvr = []*Node{this} } - *t.RecvsP() = tofunargs(rcvr) - *t.ResultsP() = tofunargs(out) - *t.ParamsP() = tofunargs(in) + *t.RecvsP() = tofunargs(rcvr, FunargRcvr) + *t.ResultsP() = tofunargs(out, FunargResults) + *t.ParamsP() = tofunargs(in, FunargParams) checkdupfields("argument", t.Recvs(), t.Results(), t.Params()) diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index bc22dfacc0f713..d7365daaea3ab6 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -640,7 +640,7 @@ func esc(e *EscState, n *Node, up *Node) { // "Big" conditions that were scattered around in walk have been gathered here if n.Esc != EscHeap && n.Type != nil && (n.Type.Width > MaxStackVarSize || - n.Op == ONEW && n.Type.Elem().Width >= 1<<16 || + (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= 1<<16 || n.Op == OMAKESLICE && !isSmallMakeSlice(n)) { if Debug['m'] > 2 { Warnl(n.Lineno, "%v is too large for stack", n) @@ -900,13 +900,13 @@ func esc(e *EscState, n *Node, up *Node) { escassignSinkNilWhy(e, n, n7.Right, "map literal value") } - // Link addresses of captured variables to closure. case OCLOSURE: + // Link addresses of captured variables to closure. for _, v := range n.Func.Cvars.Slice() { if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs continue } - a := v.Name.Param.Closure + a := v.Name.Defn if !v.Name.Byval { a = Nod(OADDR, a, nil) a.Lineno = v.Lineno @@ -1068,7 +1068,6 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { OIND, // dst = *x ODOTPTR, // dst = (*x).f ONAME, - OPARAM, ODDDARG, OPTRLIT, OARRAYLIT, @@ -1818,14 +1817,14 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, } } - // Treat a PPARAMREF closure variable as equivalent to the + // Treat a captured closure variable as equivalent to the // original variable. - if src.Class == PPARAMREF { + if src.isClosureVar() { if leaks && Debug['m'] != 0 { Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort)) step.describe(src) } - escwalk(e, level, dst, src.Name.Param.Closure, e.stepWalk(dst, src.Name.Param.Closure, "closure-var", step)) + escwalk(e, level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step)) } case OPTRLIT, OADDR: @@ -1835,20 +1834,20 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, } if leaks { src.Esc = EscHeap - addrescapes(src.Left) if Debug['m'] != 0 && osrcesc != src.Esc { p := src if p.Left.Op == OCLOSURE { p = p.Left // merely to satisfy error messages in tests } if Debug['m'] > 2 { - Warnl(src.Lineno, "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v", - Nconv(p, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth) + Warnl(src.Lineno, "%v escapes to heap, level=%v, dst=%v dst.eld=%v, src.eld=%v", + Nconv(p, FmtShort), level, dst, dstE.Escloopdepth, modSrcLoopdepth) } else { Warnl(src.Lineno, "%v escapes to heap", Nconv(p, FmtShort)) step.describe(src) } } + addrescapes(src.Left) escwalkBody(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth) extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op } else { diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 2dd137ed773c44..1148b27f025f17 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -121,7 +121,7 @@ func reexportdep(n *Node) { //print("reexportdep %+hN\n", n); switch n.Op { case ONAME: - switch n.Class &^ PHEAP { + switch n.Class { // methods will be printed along with their type // nodes for T.Method expressions case PFUNC: diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 82b84b3aa52f28..3d26a1d89b3029 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -218,9 +218,9 @@ var classnames = []string{ "Pxxx", "PEXTERN", "PAUTO", + "PAUTOHEAP", "PPARAM", "PPARAMOUT", - "PPARAMREF", "PFUNC", } @@ -251,14 +251,10 @@ func jconv(n *Node, flag FmtFlag) string { } if n.Class != 0 { - s := "" - if n.Class&PHEAP != 0 { - s = ",heap" - } - if int(n.Class&^PHEAP) < len(classnames) { - fmt.Fprintf(&buf, " class(%s%s)", classnames[n.Class&^PHEAP], s) + if int(n.Class) < len(classnames) { + fmt.Fprintf(&buf, " class(%s)", classnames[n.Class]) } else { - fmt.Fprintf(&buf, " class(%d?%s)", n.Class&^PHEAP, s) + fmt.Fprintf(&buf, " class(%d?)", n.Class) } } @@ -798,8 +794,8 @@ func stmtfmt(n *Node) string { switch n.Op { case ODCL: if fmtmode == FExp { - switch n.Left.Class &^ PHEAP { - case PPARAM, PPARAMOUT, PAUTO: + switch n.Left.Class { + case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP: f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type) goto ret } @@ -1197,7 +1193,7 @@ func exprfmt(n *Node, prec int) string { if n.Nbody.Len() != 0 { return fmt.Sprintf("%v { %v }", n.Type, n.Nbody) } - return fmt.Sprintf("%v { %v }", n.Type, n.Name.Param.Closure.Nbody) + return fmt.Sprintf("%v { %v }", n.Type, n.Func.Closure.Nbody) case OCOMPLIT: ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && n.Right.Type.IsPtr() @@ -1663,7 +1659,7 @@ func Fldconv(f *Field, flag FmtFlag) string { } if s != nil && f.Embedded == 0 { - if f.Funarg { + if f.Funarg != FunargNone { name = Nconv(f.Nname, 0) } else if flag&FmtLong != 0 { name = sconv(s, FmtShort|FmtByte) // qualify non-exported names (used on structs, not on funarg) @@ -1696,7 +1692,7 @@ func Fldconv(f *Field, flag FmtFlag) string { // (The escape analysis tags do not apply to func vars.) // But it must not suppress struct field tags. // See golang.org/issue/13777 and golang.org/issue/14331. - if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != "" { + if flag&FmtShort == 0 && (!fmtbody || f.Funarg == FunargNone) && f.Note != "" { str += " " + strconv.Quote(f.Note) } diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go index 275e6a750713a7..3faf6d4a63cc9c 100644 --- a/src/cmd/compile/internal/gc/gen.go +++ b/src/cmd/compile/internal/gc/gen.go @@ -43,52 +43,38 @@ func addrescapes(n *Node) { break } - switch n.Class { - case PPARAMREF: + // If a closure reference escapes, mark the outer variable as escaping. + if n.isClosureVar() { addrescapes(n.Name.Defn) + break + } - // if func param, need separate temporary - // to hold heap pointer. - // the function type has already been checked - // (we're in the function body) - // so the param already has a valid xoffset. - - // expression to refer to stack copy - case PPARAM, PPARAMOUT: - n.Name.Param.Stackparam = Nod(OPARAM, n, nil) - - n.Name.Param.Stackparam.Type = n.Type - n.Name.Param.Stackparam.Addable = true - if n.Xoffset == BADWIDTH { - Fatalf("addrescapes before param assignment") - } - n.Name.Param.Stackparam.Xoffset = n.Xoffset - fallthrough - - case PAUTO: - n.Class |= PHEAP - - n.Addable = false - n.Ullman = 2 - n.Xoffset = 0 - - // create stack variable to hold pointer to heap - oldfn := Curfn + if n.Class != PPARAM && n.Class != PPARAMOUT && n.Class != PAUTO { + break + } - Curfn = n.Name.Curfn - if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { - Curfn = Curfn.Func.Closure - } - n.Name.Heapaddr = temp(Ptrto(n.Type)) - buf := fmt.Sprintf("&%v", n.Sym) - n.Name.Heapaddr.Sym = Lookup(buf) - n.Name.Heapaddr.Orig.Sym = n.Name.Heapaddr.Sym - n.Esc = EscHeap - if Debug['m'] != 0 { - fmt.Printf("%v: moved to heap: %v\n", n.Line(), n) - } - Curfn = oldfn + // This is a plain parameter or local variable that needs to move to the heap, + // but possibly for the function outside the one we're compiling. + // That is, if we have: + // + // func f(x int) { + // func() { + // global = &x + // } + // } + // + // then we're analyzing the inner closure but we need to move x to the + // heap in f, not in the inner closure. Flip over to f before calling moveToHeap. + oldfn := Curfn + Curfn = n.Name.Curfn + if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { + Curfn = Curfn.Func.Closure } + ln := lineno + lineno = Curfn.Lineno + moveToHeap(n) + Curfn = oldfn + lineno = ln case OIND, ODOTPTR: break @@ -105,6 +91,110 @@ func addrescapes(n *Node) { } } +// isParamStackCopy reports whether this is the on-stack copy of a +// function parameter that moved to the heap. +func (n *Node) isParamStackCopy() bool { + return n.Op == ONAME && (n.Class == PPARAM || n.Class == PPARAMOUT) && n.Name.Heapaddr != nil +} + +// isParamHeapCopy reports whether this is the on-heap copy of +// a function parameter that moved to the heap. +func (n *Node) isParamHeapCopy() bool { + return n.Op == ONAME && n.Class == PAUTOHEAP && n.Name.Param.Stackcopy != nil +} + +// paramClass reports the parameter class (PPARAM or PPARAMOUT) +// of the node, which may be an unmoved on-stack parameter +// or the on-heap or on-stack copy of a parameter that moved to the heap. +// If the node is not a parameter, paramClass returns Pxxx. +func (n *Node) paramClass() Class { + if n.Op != ONAME { + return Pxxx + } + if n.Class == PPARAM || n.Class == PPARAMOUT { + return n.Class + } + if n.isParamHeapCopy() { + return n.Name.Param.Stackcopy.Class + } + return Pxxx +} + +// moveToHeap records the parameter or local variable n as moved to the heap. +func moveToHeap(n *Node) { + if Debug['r'] != 0 { + Dump("MOVE", n) + } + if compiling_runtime { + Yyerror("%v escapes to heap, not allowed in runtime.", n) + } + if n.Class == PAUTOHEAP { + Dump("n", n) + Fatalf("double move to heap") + } + + // Allocate a local stack variable to hold the pointer to the heap copy. + // temp will add it to the function declaration list automatically. + heapaddr := temp(Ptrto(n.Type)) + heapaddr.Sym = Lookup("&" + n.Sym.Name) + heapaddr.Orig.Sym = heapaddr.Sym + + // Parameters have a local stack copy used at function start/end + // in addition to the copy in the heap that may live longer than + // the function. + if n.Class == PPARAM || n.Class == PPARAMOUT { + if n.Xoffset == BADWIDTH { + Fatalf("addrescapes before param assignment") + } + + // We rewrite n below to be a heap variable (indirection of heapaddr). + // Preserve a copy so we can still write code referring to the original, + // and substitute that copy into the function declaration list + // so that analyses of the local (on-stack) variables use it. + stackcopy := Nod(ONAME, nil, nil) + stackcopy.Sym = n.Sym + stackcopy.Type = n.Type + stackcopy.Xoffset = n.Xoffset + stackcopy.Class = n.Class + stackcopy.Name.Heapaddr = heapaddr + if n.Class == PPARAM { + stackcopy.SetNotLiveAtEnd(true) + } + n.Name.Param.Stackcopy = stackcopy + + // Substitute the stackcopy into the function variable list so that + // liveness and other analyses use the underlying stack slot + // and not the now-pseudo-variable n. + found := false + for i, d := range Curfn.Func.Dcl { + if d == n { + Curfn.Func.Dcl[i] = stackcopy + found = true + break + } + // Parameters are before locals, so can stop early. + // This limits the search even in functions with many local variables. + if d.Class == PAUTO { + break + } + } + if !found { + Fatalf("cannot find %v in local variable list", n) + } + Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) + } + + // Modify n in place so that uses of n now mean indirection of the heapaddr. + n.Class = PAUTOHEAP + n.Ullman = 2 + n.Xoffset = 0 + n.Name.Heapaddr = heapaddr + n.Esc = EscHeap + if Debug['m'] != 0 { + fmt.Printf("%v: moved to heap: %v\n", n.Line(), n) + } +} + func clearlabels() { for _, l := range labellist { l.Sym.Label = nil @@ -243,16 +333,9 @@ func cgen_dcl(n *Node) { Fatalf("cgen_dcl") } - if n.Class&PHEAP == 0 { - return + if n.Class == PAUTOHEAP { + Fatalf("cgen_dcl %v", n) } - if compiling_runtime { - Yyerror("%v escapes to heap, not allowed in runtime.", n) - } - if prealloc[n] == nil { - prealloc[n] = callnew(n.Type) - } - Cgen_as(n.Name.Heapaddr, prealloc[n]) } // generate discard of value @@ -263,7 +346,7 @@ func cgen_discard(nr *Node) { switch nr.Op { case ONAME: - if nr.Class&PHEAP == 0 && nr.Class != PEXTERN && nr.Class != PFUNC && nr.Class != PPARAMREF { + if nr.Class != PAUTOHEAP && nr.Class != PEXTERN && nr.Class != PFUNC { gused(nr) } @@ -908,11 +991,6 @@ func Cgen_as_wb(nl, nr *Node, wb bool) { } if nr == nil || iszero(nr) { - // heaps should already be clear - if nr == nil && (nl.Class&PHEAP != 0) { - return - } - tl := nl.Type if tl == nil { return diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index f9a372dccee63d..b6b858c0d980c8 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -91,14 +91,12 @@ const ( Pxxx Class = iota PEXTERN // global variable PAUTO // local variables + PAUTOHEAP // local variable or parameter moved to heap PPARAM // input arguments PPARAMOUT // output results - PPARAMREF // closure variable reference PFUNC // global function PDISCARD // discard during parse of duplicate import - - PHEAP = 1 << 7 // an extra bit to identify an escaped variable ) // note this is the runtime representation @@ -133,6 +131,7 @@ var pragcgobuf string var infile string var outfile string +var linkobj string var bout *bio.Writer @@ -260,8 +259,6 @@ var Widthreg int var nblank *Node -var Funcdepth int32 - var typecheckok bool var compiling_runtime bool diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 603d0349d601b7..4943d9dddeda49 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -53,7 +53,6 @@ func Ismem(n *Node) bool { OCAP, OINDREG, ONAME, - OPARAM, OCLOSUREVAR: return true @@ -349,18 +348,6 @@ func Naddr(a *obj.Addr, n *Node) { a.Width = 0 } - // n->left is PHEAP ONAME for stack parameter. - // compute address of actual parameter on stack. - case OPARAM: - a.Etype = uint8(Simtype[n.Left.Type.Etype]) - - a.Width = n.Left.Type.Width - a.Offset = n.Xoffset - a.Sym = Linksym(n.Left.Sym) - a.Type = obj.TYPE_MEM - a.Name = obj.NAME_PARAM - a.Node = n.Left.Orig - case OCLOSUREVAR: if !Curfn.Func.Needctxt { Fatalf("closurevar without needctxt") @@ -528,25 +515,36 @@ func newplist() *obj.Plist { return pl } -// nodarg does something that depends on the value of -// fp (this was previously completely undocumented). +// nodarg returns a Node for the function argument denoted by t, +// which is either the entire function argument or result struct (t is a struct *Type) +// or a specific argument (t is a *Field within a struct *Type). +// +// If fp is 0, the node is for use by a caller invoking the given +// function, preparing the arguments before the call +// or retrieving the results after the call. +// In this case, the node will correspond to an outgoing argument +// slot like 8(SP). // -// fp=1 corresponds to input args -// fp=0 corresponds to output args -// fp=-1 is a special case of output args for a -// specific call from walk that previously (and -// incorrectly) passed a 1; the behavior is exactly -// the same as it is for 1, except that PARAMOUT is -// generated instead of PARAM. +// If fp is 1, the node is for use by the function itself +// (the callee), to retrieve its arguments or write its results. +// In this case the node will be an ONAME with an appropriate +// type and offset. func nodarg(t interface{}, fp int) *Node { var n *Node + var funarg Funarg switch t := t.(type) { + default: + Fatalf("bad nodarg %T(%v)", t, t) + case *Type: - // entire argument struct, not just one arg + // Entire argument struct, not just one arg if !t.IsFuncArgStruct() { Fatalf("nodarg: bad type %v", t) } + funarg = t.StructType().Funarg + + // Build fake variable name for whole arg struct. n = Nod(ONAME, nil, nil) n.Sym = Lookup(".args") n.Type = t @@ -559,15 +557,43 @@ func nodarg(t interface{}, fp int) *Node { } n.Xoffset = first.Offset n.Addable = true + case *Field: - if fp == 1 || fp == -1 { + funarg = t.Funarg + if fp == 1 { + // NOTE(rsc): This should be using t.Nname directly, + // except in the case where t.Nname.Sym is the blank symbol and + // so the assignment would be discarded during code generation. + // In that case we need to make a new node, and there is no harm + // in optimization passes to doing so. But otherwise we should + // definitely be using the actual declaration and not a newly built node. + // The extra Fatalf checks here are verifying that this is the case, + // without changing the actual logic (at time of writing, it's getting + // toward time for the Go 1.7 beta). + // At some quieter time (assuming we've never seen these Fatalfs happen) + // we could change this code to use "expect" directly. + expect := t.Nname + if expect.isParamHeapCopy() { + expect = expect.Name.Param.Stackcopy + } + for _, n := range Curfn.Func.Dcl { if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { + if n != expect { + Fatalf("nodarg: unexpected node: %v (%p %v) vs %v (%p %v)", n, n, n.Op, t.Nname, t.Nname, t.Nname.Op) + } return n } } + + if !isblanksym(expect.Sym) { + Fatalf("nodarg: did not find node in dcl list: %v", expect) + } } + // Build fake name for individual variable. + // This is safe because if there was a real declared name + // we'd have used it above. n = Nod(ONAME, nil, nil) n.Type = t.Type n.Sym = t.Sym @@ -577,8 +603,6 @@ func nodarg(t interface{}, fp int) *Node { n.Xoffset = t.Offset n.Addable = true n.Orig = t.Nname - default: - panic("unreachable") } // Rewrite argument named _ to __, @@ -589,23 +613,23 @@ func nodarg(t interface{}, fp int) *Node { } switch fp { - case 0: // output arg - n.Op = OINDREG + default: + Fatalf("bad fp") + case 0: // preparing arguments for call + n.Op = OINDREG n.Reg = int16(Thearch.REGSP) n.Xoffset += Ctxt.FixedFrameSize() - case 1: // input arg + case 1: // reading arguments inside call n.Class = PPARAM - - case -1: // output arg from paramstoheap - n.Class = PPARAMOUT - - case 2: // offset output arg - Fatalf("shouldn't be used") + if funarg == FunargResults { + n.Class = PPARAMOUT + } } n.Typecheck = 1 + n.Addrtaken = true // keep optimizers at bay return n } diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index 6c9223b57a1b25..67a050a9cad136 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -31,21 +31,22 @@ func renameinit() *Sym { } // hand-craft the following initialization code -// var initdone· uint8 (1) -// func init() (2) +// var initdone· uint8 (1) +// func init() { (2) // if initdone· > 1 { (3) // return (3a) -// if initdone· == 1 { (4) -// throw(); (4a) -// } -// initdone· = 1; (6) -// // over all matching imported symbols -// .init() (7) -// { } (8) -// init.() // if any (9) -// initdone· = 2; (10) -// return (11) -// } +// } +// if initdone· == 1 { (4) +// throw() (4a) +// } +// initdone· = 1 (5) +// // over all matching imported symbols +// .init() (6) +// { } (7) +// init.() // if any (8) +// initdone· = 2 (9) +// return (10) +// } func anyinit(n []*Node) bool { // are there any interesting init statements for _, ln := range n { @@ -132,12 +133,12 @@ func fninit(n []*Node) { // (4a) b.Nbody.Set1(Nod(OCALL, syslook("throwinit"), nil)) - // (6) + // (5) a = Nod(OAS, gatevar, Nodintconst(1)) r = append(r, a) - // (7) + // (6) for _, s := range initSyms { if s.Def != nil && s != initsym { // could check that it is fn of no args/returns @@ -146,10 +147,10 @@ func fninit(n []*Node) { } } - // (8) + // (7) r = append(r, nf...) - // (9) + // (8) // could check that it is fn of no args/returns for i := 1; ; i++ { s := LookupN("init.", i) @@ -160,12 +161,12 @@ func fninit(n []*Node) { r = append(r, a) } - // (10) + // (9) a = Nod(OAS, gatevar, Nodintconst(2)) r = append(r, a) - // (11) + // (10) a = Nod(ORETURN, nil, nil) r = append(r, a) diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 95ba56edd2288c..0c1b05079c511f 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -27,9 +27,7 @@ package gc -import ( - "fmt" -) +import "fmt" // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods // the ->sym can be re-used in the local package, so peel it off the receiver's type. @@ -180,6 +178,7 @@ func ishairy(n *Node, budget *int32) bool { *budget -= fn.InlCost break } + if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions if d := n.Left.Sym.Def; d != nil && d.Func.Inl.Len() != 0 { *budget -= d.Func.InlCost @@ -568,14 +567,13 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node { if ln.Class == PPARAMOUT { // return values handled below. continue } + if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap + continue + } if ln.Op == ONAME { - ln.Name.Inlvar = inlvar(ln) - - // Typecheck because inlvar is not necessarily a function parameter. - ln.Name.Inlvar = typecheck(ln.Name.Inlvar, Erv) - - if ln.Class&^PHEAP != PAUTO { - ninit.Append(Nod(ODCL, ln.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs + ln.Name.Inlvar = typecheck(inlvar(ln), Erv) + if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM { + ninit.Append(Nod(ODCL, ln.Name.Inlvar, nil)) } } } diff --git a/src/cmd/compile/internal/gc/logic_test.go b/src/cmd/compile/internal/gc/logic_test.go new file mode 100644 index 00000000000000..78d2dd2fa81fad --- /dev/null +++ b/src/cmd/compile/internal/gc/logic_test.go @@ -0,0 +1,289 @@ +package gc + +import "testing" + +// Tests to make sure logic simplification rules are correct. + +func TestLogic64(t *testing.T) { + // test values to determine function equality + values := [...]int64{-1 << 63, 1<<63 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4} + + // golden functions we use repeatedly + zero := func(x int64) int64 { return 0 } + id := func(x int64) int64 { return x } + or := func(x, y int64) int64 { return x | y } + and := func(x, y int64) int64 { return x & y } + y := func(x, y int64) int64 { return y } + + for _, test := range [...]struct { + name string + f func(int64) int64 + golden func(int64) int64 + }{ + {"x|x", func(x int64) int64 { return x | x }, id}, + {"x|0", func(x int64) int64 { return x | 0 }, id}, + {"x|-1", func(x int64) int64 { return x | -1 }, func(x int64) int64 { return -1 }}, + {"x&x", func(x int64) int64 { return x & x }, id}, + {"x&0", func(x int64) int64 { return x & 0 }, zero}, + {"x&-1", func(x int64) int64 { return x & -1 }, id}, + {"x^x", func(x int64) int64 { return x ^ x }, zero}, + {"x^0", func(x int64) int64 { return x ^ 0 }, id}, + {"x^-1", func(x int64) int64 { return x ^ -1 }, func(x int64) int64 { return ^x }}, + {"x+0", func(x int64) int64 { return x + 0 }, id}, + {"x-x", func(x int64) int64 { return x - x }, zero}, + {"x*0", func(x int64) int64 { return x * 0 }, zero}, + {"^^x", func(x int64) int64 { return ^^x }, id}, + } { + for _, v := range values { + got := test.f(v) + want := test.golden(v) + if want != got { + t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want) + } + } + } + for _, test := range [...]struct { + name string + f func(int64, int64) int64 + golden func(int64, int64) int64 + }{ + {"x|(x|y)", func(x, y int64) int64 { return x | (x | y) }, or}, + {"x|(y|x)", func(x, y int64) int64 { return x | (y | x) }, or}, + {"(x|y)|x", func(x, y int64) int64 { return (x | y) | x }, or}, + {"(y|x)|x", func(x, y int64) int64 { return (y | x) | x }, or}, + {"x&(x&y)", func(x, y int64) int64 { return x & (x & y) }, and}, + {"x&(y&x)", func(x, y int64) int64 { return x & (y & x) }, and}, + {"(x&y)&x", func(x, y int64) int64 { return (x & y) & x }, and}, + {"(y&x)&x", func(x, y int64) int64 { return (y & x) & x }, and}, + {"x^(x^y)", func(x, y int64) int64 { return x ^ (x ^ y) }, y}, + {"x^(y^x)", func(x, y int64) int64 { return x ^ (y ^ x) }, y}, + {"(x^y)^x", func(x, y int64) int64 { return (x ^ y) ^ x }, y}, + {"(y^x)^x", func(x, y int64) int64 { return (y ^ x) ^ x }, y}, + {"-(y-x)", func(x, y int64) int64 { return -(y - x) }, func(x, y int64) int64 { return x - y }}, + {"(x+y)-x", func(x, y int64) int64 { return (x + y) - x }, y}, + {"(y+x)-x", func(x, y int64) int64 { return (y + x) - x }, y}, + } { + for _, v := range values { + for _, w := range values { + got := test.f(v, w) + want := test.golden(v, w) + if want != got { + t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want) + } + } + } + } +} + +func TestLogic32(t *testing.T) { + // test values to determine function equality + values := [...]int32{-1 << 31, 1<<31 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4} + + // golden functions we use repeatedly + zero := func(x int32) int32 { return 0 } + id := func(x int32) int32 { return x } + or := func(x, y int32) int32 { return x | y } + and := func(x, y int32) int32 { return x & y } + y := func(x, y int32) int32 { return y } + + for _, test := range [...]struct { + name string + f func(int32) int32 + golden func(int32) int32 + }{ + {"x|x", func(x int32) int32 { return x | x }, id}, + {"x|0", func(x int32) int32 { return x | 0 }, id}, + {"x|-1", func(x int32) int32 { return x | -1 }, func(x int32) int32 { return -1 }}, + {"x&x", func(x int32) int32 { return x & x }, id}, + {"x&0", func(x int32) int32 { return x & 0 }, zero}, + {"x&-1", func(x int32) int32 { return x & -1 }, id}, + {"x^x", func(x int32) int32 { return x ^ x }, zero}, + {"x^0", func(x int32) int32 { return x ^ 0 }, id}, + {"x^-1", func(x int32) int32 { return x ^ -1 }, func(x int32) int32 { return ^x }}, + {"x+0", func(x int32) int32 { return x + 0 }, id}, + {"x-x", func(x int32) int32 { return x - x }, zero}, + {"x*0", func(x int32) int32 { return x * 0 }, zero}, + {"^^x", func(x int32) int32 { return ^^x }, id}, + } { + for _, v := range values { + got := test.f(v) + want := test.golden(v) + if want != got { + t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want) + } + } + } + for _, test := range [...]struct { + name string + f func(int32, int32) int32 + golden func(int32, int32) int32 + }{ + {"x|(x|y)", func(x, y int32) int32 { return x | (x | y) }, or}, + {"x|(y|x)", func(x, y int32) int32 { return x | (y | x) }, or}, + {"(x|y)|x", func(x, y int32) int32 { return (x | y) | x }, or}, + {"(y|x)|x", func(x, y int32) int32 { return (y | x) | x }, or}, + {"x&(x&y)", func(x, y int32) int32 { return x & (x & y) }, and}, + {"x&(y&x)", func(x, y int32) int32 { return x & (y & x) }, and}, + {"(x&y)&x", func(x, y int32) int32 { return (x & y) & x }, and}, + {"(y&x)&x", func(x, y int32) int32 { return (y & x) & x }, and}, + {"x^(x^y)", func(x, y int32) int32 { return x ^ (x ^ y) }, y}, + {"x^(y^x)", func(x, y int32) int32 { return x ^ (y ^ x) }, y}, + {"(x^y)^x", func(x, y int32) int32 { return (x ^ y) ^ x }, y}, + {"(y^x)^x", func(x, y int32) int32 { return (y ^ x) ^ x }, y}, + {"-(y-x)", func(x, y int32) int32 { return -(y - x) }, func(x, y int32) int32 { return x - y }}, + {"(x+y)-x", func(x, y int32) int32 { return (x + y) - x }, y}, + {"(y+x)-x", func(x, y int32) int32 { return (y + x) - x }, y}, + } { + for _, v := range values { + for _, w := range values { + got := test.f(v, w) + want := test.golden(v, w) + if want != got { + t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want) + } + } + } + } +} + +func TestLogic16(t *testing.T) { + // test values to determine function equality + values := [...]int16{-1 << 15, 1<<15 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4} + + // golden functions we use repeatedly + zero := func(x int16) int16 { return 0 } + id := func(x int16) int16 { return x } + or := func(x, y int16) int16 { return x | y } + and := func(x, y int16) int16 { return x & y } + y := func(x, y int16) int16 { return y } + + for _, test := range [...]struct { + name string + f func(int16) int16 + golden func(int16) int16 + }{ + {"x|x", func(x int16) int16 { return x | x }, id}, + {"x|0", func(x int16) int16 { return x | 0 }, id}, + {"x|-1", func(x int16) int16 { return x | -1 }, func(x int16) int16 { return -1 }}, + {"x&x", func(x int16) int16 { return x & x }, id}, + {"x&0", func(x int16) int16 { return x & 0 }, zero}, + {"x&-1", func(x int16) int16 { return x & -1 }, id}, + {"x^x", func(x int16) int16 { return x ^ x }, zero}, + {"x^0", func(x int16) int16 { return x ^ 0 }, id}, + {"x^-1", func(x int16) int16 { return x ^ -1 }, func(x int16) int16 { return ^x }}, + {"x+0", func(x int16) int16 { return x + 0 }, id}, + {"x-x", func(x int16) int16 { return x - x }, zero}, + {"x*0", func(x int16) int16 { return x * 0 }, zero}, + {"^^x", func(x int16) int16 { return ^^x }, id}, + } { + for _, v := range values { + got := test.f(v) + want := test.golden(v) + if want != got { + t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want) + } + } + } + for _, test := range [...]struct { + name string + f func(int16, int16) int16 + golden func(int16, int16) int16 + }{ + {"x|(x|y)", func(x, y int16) int16 { return x | (x | y) }, or}, + {"x|(y|x)", func(x, y int16) int16 { return x | (y | x) }, or}, + {"(x|y)|x", func(x, y int16) int16 { return (x | y) | x }, or}, + {"(y|x)|x", func(x, y int16) int16 { return (y | x) | x }, or}, + {"x&(x&y)", func(x, y int16) int16 { return x & (x & y) }, and}, + {"x&(y&x)", func(x, y int16) int16 { return x & (y & x) }, and}, + {"(x&y)&x", func(x, y int16) int16 { return (x & y) & x }, and}, + {"(y&x)&x", func(x, y int16) int16 { return (y & x) & x }, and}, + {"x^(x^y)", func(x, y int16) int16 { return x ^ (x ^ y) }, y}, + {"x^(y^x)", func(x, y int16) int16 { return x ^ (y ^ x) }, y}, + {"(x^y)^x", func(x, y int16) int16 { return (x ^ y) ^ x }, y}, + {"(y^x)^x", func(x, y int16) int16 { return (y ^ x) ^ x }, y}, + {"-(y-x)", func(x, y int16) int16 { return -(y - x) }, func(x, y int16) int16 { return x - y }}, + {"(x+y)-x", func(x, y int16) int16 { return (x + y) - x }, y}, + {"(y+x)-x", func(x, y int16) int16 { return (y + x) - x }, y}, + } { + for _, v := range values { + for _, w := range values { + got := test.f(v, w) + want := test.golden(v, w) + if want != got { + t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want) + } + } + } + } +} + +func TestLogic8(t *testing.T) { + // test values to determine function equality + values := [...]int8{-1 << 7, 1<<7 - 1, -4, -3, -2, -1, 0, 1, 2, 3, 4} + + // golden functions we use repeatedly + zero := func(x int8) int8 { return 0 } + id := func(x int8) int8 { return x } + or := func(x, y int8) int8 { return x | y } + and := func(x, y int8) int8 { return x & y } + y := func(x, y int8) int8 { return y } + + for _, test := range [...]struct { + name string + f func(int8) int8 + golden func(int8) int8 + }{ + {"x|x", func(x int8) int8 { return x | x }, id}, + {"x|0", func(x int8) int8 { return x | 0 }, id}, + {"x|-1", func(x int8) int8 { return x | -1 }, func(x int8) int8 { return -1 }}, + {"x&x", func(x int8) int8 { return x & x }, id}, + {"x&0", func(x int8) int8 { return x & 0 }, zero}, + {"x&-1", func(x int8) int8 { return x & -1 }, id}, + {"x^x", func(x int8) int8 { return x ^ x }, zero}, + {"x^0", func(x int8) int8 { return x ^ 0 }, id}, + {"x^-1", func(x int8) int8 { return x ^ -1 }, func(x int8) int8 { return ^x }}, + {"x+0", func(x int8) int8 { return x + 0 }, id}, + {"x-x", func(x int8) int8 { return x - x }, zero}, + {"x*0", func(x int8) int8 { return x * 0 }, zero}, + {"^^x", func(x int8) int8 { return ^^x }, id}, + } { + for _, v := range values { + got := test.f(v) + want := test.golden(v) + if want != got { + t.Errorf("[%s](%d)=%d, want %d", test.name, v, got, want) + } + } + } + for _, test := range [...]struct { + name string + f func(int8, int8) int8 + golden func(int8, int8) int8 + }{ + {"x|(x|y)", func(x, y int8) int8 { return x | (x | y) }, or}, + {"x|(y|x)", func(x, y int8) int8 { return x | (y | x) }, or}, + {"(x|y)|x", func(x, y int8) int8 { return (x | y) | x }, or}, + {"(y|x)|x", func(x, y int8) int8 { return (y | x) | x }, or}, + {"x&(x&y)", func(x, y int8) int8 { return x & (x & y) }, and}, + {"x&(y&x)", func(x, y int8) int8 { return x & (y & x) }, and}, + {"(x&y)&x", func(x, y int8) int8 { return (x & y) & x }, and}, + {"(y&x)&x", func(x, y int8) int8 { return (y & x) & x }, and}, + {"x^(x^y)", func(x, y int8) int8 { return x ^ (x ^ y) }, y}, + {"x^(y^x)", func(x, y int8) int8 { return x ^ (y ^ x) }, y}, + {"(x^y)^x", func(x, y int8) int8 { return (x ^ y) ^ x }, y}, + {"(y^x)^x", func(x, y int8) int8 { return (y ^ x) ^ x }, y}, + {"-(y-x)", func(x, y int8) int8 { return -(y - x) }, func(x, y int8) int8 { return x - y }}, + {"(x+y)-x", func(x, y int8) int8 { return (x + y) - x }, y}, + {"(y+x)-x", func(x, y int8) int8 { return (y + x) - x }, y}, + } { + for _, v := range values { + for _, w := range values { + got := test.f(v, w) + want := test.golden(v, w) + if want != got { + t.Errorf("[%s](%d,%d)=%d, want %d", test.name, v, w, got, want) + } + } + } + } +} diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 54211e4892ddef..8ad3300dbed3e2 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -178,6 +178,7 @@ func Main() { flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`") obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) obj.Flagcount("l", "disable inlining", &Debug['l']) + flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`") obj.Flagcount("live", "debug liveness analysis", &debuglive) obj.Flagcount("m", "print optimization decisions", &Debug['m']) flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer") @@ -772,7 +773,7 @@ func importfile(f *Val, indent []byte) { if p != "empty archive" { if !strings.HasPrefix(p, "go object ") { - Yyerror("import %s: not a go object file", file) + Yyerror("import %s: not a go object file: %s", file, p) errorexit() } @@ -783,6 +784,21 @@ func importfile(f *Val, indent []byte) { } } + // process header lines + for { + p, err = imp.ReadString('\n') + if err != nil { + log.Fatalf("reading input: %v", err) + } + if p == "\n" { + break // header ends with blank line + } + if strings.HasPrefix(p, "safe") { + importpkg.Safe = true + break // ok to ignore rest + } + } + // assume files move (get installed) // so don't record the full path. linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib diff --git a/src/cmd/compile/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go index eb0e6681fa895d..58cbd240d27584 100644 --- a/src/cmd/compile/internal/gc/mkbuiltin.go +++ b/src/cmd/compile/internal/gc/mkbuiltin.go @@ -8,6 +8,7 @@ // Run this after changing builtin/runtime.go and builtin/unsafe.go // or after changing the export metadata format in the compiler. // Either way, you need to have a working compiler binary first. +// See bexport.go for how to make an export metadata format change. package main import ( diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index ae23f9557410a3..b5c06d165d4c6d 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -22,7 +22,34 @@ func formathdr(arhdr []byte, name string, size int64) { copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size)) } +// These modes say which kind of object file to generate. +// The default use of the toolchain is to set both bits, +// generating a combined compiler+linker object, one that +// serves to describe the package to both the compiler and the linker. +// In fact the compiler and linker read nearly disjoint sections of +// that file, though, so in a distributed build setting it can be more +// efficient to split the output into two files, supplying the compiler +// object only to future compilations and the linker object only to +// future links. +// +// By default a combined object is written, but if -linkobj is specified +// on the command line then the default -o output is a compiler object +// and the -linkobj output is a linker object. +const ( + modeCompilerObj = 1 << iota + modeLinkerObj +) + func dumpobj() { + if linkobj == "" { + dumpobj1(outfile, modeCompilerObj|modeLinkerObj) + } else { + dumpobj1(outfile, modeCompilerObj) + dumpobj1(linkobj, modeLinkerObj) + } +} + +func dumpobj1(outfile string, mode int) { var err error bout, err = bio.Create(outfile) if err != nil { @@ -40,8 +67,27 @@ func dumpobj() { startobj = bout.Offset() } - fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) - dumpexport() + printheader := func() { + fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) + if buildid != "" { + fmt.Fprintf(bout, "build id %q\n", buildid) + } + if localpkg.Name == "main" { + fmt.Fprintf(bout, "main\n") + } + if safemode { + fmt.Fprintf(bout, "safe\n") + } else { + fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe" + } + fmt.Fprintf(bout, "\n") // header ends with blank line + } + + printheader() + + if mode&modeCompilerObj != 0 { + dumpexport() + } if writearchive { bout.Flush() @@ -53,12 +99,20 @@ func dumpobj() { formathdr(arhdr[:], "__.PKGDEF", size) bout.Write(arhdr[:]) bout.Flush() - bout.Seek(startobj+size+(size&1), 0) + } + + if mode&modeLinkerObj == 0 { + bout.Close() + return + } + + if writearchive { + // start object file arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = bout.Offset() - fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) + printheader() } if pragcgobuf != "" { diff --git a/src/cmd/compile/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go index 015baa2376a1b6..bcdae6c76272e4 100644 --- a/src/cmd/compile/internal/gc/opnames.go +++ b/src/cmd/compile/internal/gc/opnames.go @@ -76,7 +76,6 @@ var opnames = []string{ OINDEX: "INDEX", OINDEXMAP: "INDEXMAP", OKEY: "KEY", - OPARAM: "PARAM", OLEN: "LEN", OMAKE: "MAKE", OMAKECHAN: "MAKECHAN", diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 7026ad79efa383..da334a1558cfd4 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -1082,6 +1082,20 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node { n.Left = orderaddrtemp(n.Left, order) } + case OCONVNOP: + if n.Type.IsKind(TUNSAFEPTR) && n.Left.Type.IsKind(TUINTPTR) && (n.Left.Op == OCALLFUNC || n.Left.Op == OCALLINTER || n.Left.Op == OCALLMETH) { + // When reordering unsafe.Pointer(f()) into a separate + // statement, the conversion and function call must stay + // together. See golang.org/issue/15329. + orderinit(n.Left, order) + ordercall(n.Left, order) + if lhs == nil || lhs.Op != ONAME || instrumenting { + n = ordercopyexpr(n, n.Type, order, 0) + } + } else { + n.Left = orderexpr(n.Left, order, nil) + } + case OANDAND, OOROR: mark := marktemp(order) n.Left = orderexpr(n.Left, order, nil) diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go index 55f352590bb351..7ffd42f83cba51 100644 --- a/src/cmd/compile/internal/gc/parser.go +++ b/src/cmd/compile/internal/gc/parser.go @@ -398,11 +398,8 @@ func (p *parser) import_package() { p.import_error() } - importsafe := false + // read but skip "safe" bit (see issue #15772) if p.tok == LNAME { - if p.sym_.Name == "safe" { - importsafe = true - } p.next() } p.want(';') @@ -413,7 +410,6 @@ func (p *parser) import_package() { } else if importpkg.Name != name { Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path) } - importpkg.Safe = importsafe typecheckok = true defercheckwidth() diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index 87f4a11c0077c3..85138c9fcde140 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -197,54 +197,41 @@ func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool { return false } -// Collects and returns a slice of *Nodes for functions arguments and local -// variables. +// livenessShouldTrack reports whether the liveness analysis +// should track the variable n. +// We don't care about variables that have no pointers, +// nor do we care about non-local variables, +// nor do we care about empty structs (handled by the pointer check), +// nor do we care about the fake PAUTOHEAP variables. +func livenessShouldTrack(n *Node) bool { + return n.Op == ONAME && (n.Class == PAUTO || n.Class == PPARAM || n.Class == PPARAMOUT) && haspointers(n.Type) +} + +// getvariables returns the list of on-stack variables that we need to track. func getvariables(fn *Node) []*Node { - var result []*Node - for _, ln := range fn.Func.Dcl { - if ln.Op == ONAME { - // In order for GODEBUG=gcdead=1 to work, each bitmap needs - // to contain information about all variables covered by the bitmap. - // For local variables, the bitmap only covers the stkptrsize - // bytes in the frame where variables containing pointers live. - // For arguments and results, the bitmap covers all variables, - // so we must include all the variables, even the ones without - // pointers. - // + var vars []*Node + for _, n := range fn.Func.Dcl { + if n.Op == ONAME { // The Node.opt field is available for use by optimization passes. - // We use it to hold the index of the node in the variables array, plus 1 - // (so that 0 means the Node is not in the variables array). - // Each pass should clear opt when done, but you never know, - // so clear them all ourselves too. + // We use it to hold the index of the node in the variables array + // (nil means the Node is not in the variables array). // The Node.curfn field is supposed to be set to the current function // already, but for some compiler-introduced names it seems not to be, // so fix that here. // Later, when we want to find the index of a node in the variables list, - // we will check that n.curfn == curfn and n.opt > 0. Then n.opt - 1 + // we will check that n.Curfn == Curfn and n.Opt() != nil. Then n.Opt().(int32) // is the index in the variables list. - ln.SetOpt(nil) - - // The compiler doesn't emit initializations for zero-width parameters or results. - if ln.Type.Width == 0 { - continue - } - - ln.Name.Curfn = Curfn - switch ln.Class { - case PAUTO: - if haspointers(ln.Type) { - ln.SetOpt(int32(len(result))) - result = append(result, ln) - } + n.SetOpt(nil) + n.Name.Curfn = Curfn + } - case PPARAM, PPARAMOUT: - ln.SetOpt(int32(len(result))) - result = append(result, ln) - } + if livenessShouldTrack(n) { + n.SetOpt(int32(len(vars))) + vars = append(vars, n) } } - return result + return vars } // A pretty printer for control flow graphs. Takes a slice of *BasicBlocks. @@ -567,9 +554,11 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini // read the out arguments - they won't be set until the new // function runs. for i, node := range vars { - switch node.Class &^ PHEAP { + switch node.Class { case PPARAM: - bvset(uevar, int32(i)) + if !node.NotLiveAtEnd() { + bvset(uevar, int32(i)) + } // If the result had its address taken, it is being tracked // by the avarinit code, which does not use uevar. @@ -593,7 +582,7 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini // A text instruction marks the entry point to a function and // the definition point of all in arguments. for i, node := range vars { - switch node.Class &^ PHEAP { + switch node.Class { case PPARAM: if node.Addrtaken { bvset(avarinit, int32(i)) @@ -607,24 +596,17 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 { from := &prog.From - if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Name.Curfn == Curfn { - switch ((from.Node).(*Node)).Class &^ PHEAP { - case PAUTO, PPARAM, PPARAMOUT: - pos, ok := from.Node.(*Node).Opt().(int32) // index in vars - if !ok { - break - } - if pos >= int32(len(vars)) || vars[pos] != from.Node { - Fatalf("bad bookkeeping in liveness %v %d", Nconv(from.Node.(*Node), 0), pos) - } - if ((from.Node).(*Node)).Addrtaken { + if from.Node != nil && from.Sym != nil { + n := from.Node.(*Node) + if pos := liveIndex(n, vars); pos >= 0 { + if n.Addrtaken { bvset(avarinit, pos) } else { if prog.Info.Flags&(LeftRead|LeftAddr) != 0 { bvset(uevar, pos) } if prog.Info.Flags&LeftWrite != 0 { - if from.Node != nil && !Isfat(((from.Node).(*Node)).Type) { + if !Isfat(n.Type) { bvset(varkill, pos) } } @@ -635,17 +617,10 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 { to := &prog.To - if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Name.Curfn == Curfn { - switch ((to.Node).(*Node)).Class &^ PHEAP { - case PAUTO, PPARAM, PPARAMOUT: - pos, ok := to.Node.(*Node).Opt().(int32) // index in vars - if !ok { - return - } - if pos >= int32(len(vars)) || vars[pos] != to.Node { - Fatalf("bad bookkeeping in liveness %v %d", Nconv(to.Node.(*Node), 0), pos) - } - if ((to.Node).(*Node)).Addrtaken { + if to.Node != nil && to.Sym != nil { + n := to.Node.(*Node) + if pos := liveIndex(n, vars); pos >= 0 { + if n.Addrtaken { if prog.As != obj.AVARKILL { bvset(avarinit, pos) } @@ -665,7 +640,7 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini bvset(uevar, pos) } if prog.Info.Flags&RightWrite != 0 { - if to.Node != nil && (!Isfat(((to.Node).(*Node)).Type) || prog.As == obj.AVARDEF) { + if !Isfat(n.Type) || prog.As == obj.AVARDEF { bvset(varkill, pos) } } @@ -675,6 +650,24 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini } } +// liveIndex returns the index of n in the set of tracked vars. +// If n is not a tracked var, liveIndex returns -1. +// If n is not a tracked var but should be tracked, liveIndex crashes. +func liveIndex(n *Node, vars []*Node) int32 { + if n.Name.Curfn != Curfn || !livenessShouldTrack(n) { + return -1 + } + + pos, ok := n.Opt().(int32) // index in vars + if !ok { + Fatalf("lost track of variable in liveness: %v (%p, %p)", n, n, n.Orig) + } + if pos >= int32(len(vars)) || vars[pos] != n { + Fatalf("bad bookkeeping in liveness: %v (%p, %p)", n, n, n.Orig) + } + return pos +} + // Constructs a new liveness structure used to hold the global state of the // liveness computation. The cfg argument is a slice of *BasicBlocks and the // vars argument is a slice of *Nodes. @@ -812,8 +805,7 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) { return } for _, a := range fn.Func.Dcl { - class := a.Class &^ PHEAP - if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n { + if a.Op == ONAME && (a.Class == PPARAM || a.Class == PPARAMOUT) && a == n { return } } @@ -980,23 +972,6 @@ func onebitlivepointermap(lv *Liveness, liveout bvec, vars []*Node, args bvec, l onebitwalktype1(node.Type, &xoffset, args) } } - - // The node list only contains declared names. - // If the receiver or arguments are unnamed, they will be omitted - // from the list above. Preserve those values - even though they are unused - - // in order to keep their addresses live for use in stack traces. - thisargtype := lv.fn.Type.Recvs() - - if thisargtype != nil { - xoffset = 0 - onebitwalktype1(thisargtype, &xoffset, args) - } - - inargtype := lv.fn.Type.Params() - if inargtype != nil { - xoffset = 0 - onebitwalktype1(inargtype, &xoffset, args) - } } // Construct a disembodied instruction. diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go index 5bcaf89d50173a..4a658b197665e9 100644 --- a/src/cmd/compile/internal/gc/racewalk.go +++ b/src/cmd/compile/internal/gc/racewalk.go @@ -419,7 +419,6 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) { case OPRINT, // don't bother instrumenting it OPRINTN, // don't bother instrumenting it OCHECKNIL, // always followed by a read. - OPARAM, // it appears only in fn->exit to copy heap params back OCLOSUREVAR, // immutable pointer to captured variable ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) OINDREG, // at this stage, only n(SP) nodes from nodarg @@ -496,7 +495,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool { // e.g. if we've got a local variable/method receiver // that has got a pointer inside. Whether it points to // the heap or not is impossible to know at compile time - if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { + if class == PAUTOHEAP || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { hascalls := 0 foreach(n, hascallspred, &hascalls) if hascalls != 0 { diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index c6f2acffbfcbb1..4469d71f1c56cc 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -516,7 +516,7 @@ func isliteral(n *Node) bool { } func (n *Node) isSimpleName() bool { - return n.Op == ONAME && n.Addable && n.Class&PHEAP == 0 && n.Class != PPARAMREF + return n.Op == ONAME && n.Addable && n.Class != PAUTOHEAP } func litas(l *Node, r *Node, init *Nodes) { diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index a01da13883e5a9..c474c47ddbdced 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -23,7 +23,7 @@ func TestSizeof(t *testing.T) { _64bit uintptr // size on 64bit platforms }{ {Flow{}, 52, 88}, - {Func{}, 96, 168}, + {Func{}, 92, 160}, {Name{}, 52, 80}, {Node{}, 92, 144}, {Sym{}, 60, 112}, diff --git a/src/cmd/compile/internal/gc/sparselocatephifunctions.go b/src/cmd/compile/internal/gc/sparselocatephifunctions.go new file mode 100644 index 00000000000000..e15f22123f7262 --- /dev/null +++ b/src/cmd/compile/internal/gc/sparselocatephifunctions.go @@ -0,0 +1,199 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gc + +import ( + "cmd/compile/internal/ssa" + "fmt" + "math" +) + +// sparseDefState contains a Go map from ONAMEs (*Node) to sparse definition trees, and +// a search helper for the CFG's dominator tree in which those definitions are embedded. +// Once initialized, given a use of an ONAME within a block, the ssa definition for +// that ONAME can be discovered in time roughly proportional to the log of the number +// of SSA definitions of that ONAME (thus avoiding pathological quadratic behavior for +// very large programs). The helper contains state (a dominator tree numbering) common +// to all the sparse definition trees, as well as some necessary data obtained from +// the ssa package. +// +// This algorithm has improved asymptotic complexity, but the constant factor is +// rather large and thus it is only preferred for very large inputs containing +// 1000s of blocks and variables. +type sparseDefState struct { + helper *ssa.SparseTreeHelper // contains one copy of information needed to do sparse mapping + defmapForOname map[*Node]*onameDefs // for each ONAME, its definition set (normal and phi) +} + +// onameDefs contains a record of definitions (ordinary and implied phi function) for a single OName. +// stm is the set of definitions for the OName. +// firstdef and lastuse are postorder block numberings that +// conservatively bracket the entire lifetime of the OName. +type onameDefs struct { + stm *ssa.SparseTreeMap + // firstdef and lastuse define an interval in the postorder numbering + // that is guaranteed to include the entire lifetime of an ONAME. + // In the postorder numbering, math.MaxInt32 is before anything, + // and 0 is after-or-equal all exit nodes and infinite loops. + firstdef int32 // the first definition of this ONAME *in the postorder numbering* + lastuse int32 // the last use of this ONAME *in the postorder numbering* +} + +// defsFor finds or creates-and-inserts-in-map the definition information +// (sparse tree and live range) for a given OName. +func (m *sparseDefState) defsFor(n *Node) *onameDefs { + d := m.defmapForOname[n] + if d != nil { + return d + } + // Reminder: firstdef/lastuse are postorder indices, not block indices, + // so these default values define an empty interval, not the entire one. + d = &onameDefs{stm: m.helper.NewTree(), firstdef: 0, lastuse: math.MaxInt32} + m.defmapForOname[n] = d + return d +} + +// Insert adds a definition at b (with specified before/within/after adjustment) +// to sparse tree onameDefs. The lifetime is extended as necessary. +func (m *sparseDefState) Insert(tree *onameDefs, b *ssa.Block, adjust int32) { + bponum := m.helper.Ponums[b.ID] + if bponum > tree.firstdef { + tree.firstdef = bponum + } + tree.stm.Insert(b, adjust, b, m.helper) +} + +// Use updates tree to record a use within b, extending the lifetime as necessary. +func (m *sparseDefState) Use(tree *onameDefs, b *ssa.Block) { + bponum := m.helper.Ponums[b.ID] + if bponum < tree.lastuse { + tree.lastuse = bponum + } +} + +// locatePotentialPhiFunctions finds all the places where phi functions +// will be inserted into a program and records those and ordinary definitions +// in a "map" (not a Go map) that given an OName and use site, returns the +// SSA definition for that OName that will reach the use site (that is, +// the use site's nearest def/phi site in the dominator tree.) +func (s *state) locatePotentialPhiFunctions(fn *Node) *sparseDefState { + // s.config.SparsePhiCutoff() is compared with product of numblocks and numvalues, + // if product is smaller than cutoff, use old non-sparse method. + // cutoff == 0 implies all sparse + // cutoff == uint(-1) implies all non-sparse + if uint64(s.f.NumValues())*uint64(s.f.NumBlocks()) < s.config.SparsePhiCutoff() { + return nil + } + + helper := ssa.NewSparseTreeHelper(s.f) + po := helper.Po // index by block.ID to obtain postorder # of block. + trees := make(map[*Node]*onameDefs) + dm := &sparseDefState{defmapForOname: trees, helper: helper} + + // Process params, taking note of their special lifetimes + b := s.f.Entry + for _, n := range fn.Func.Dcl { + switch n.Class { + case PPARAM, PPARAMOUT: + t := dm.defsFor(n) + dm.Insert(t, b, ssa.AdjustBefore) // define param at entry block + if n.Class == PPARAMOUT { + dm.Use(t, po[0]) // Explicitly use PPARAMOUT at very last block + } + default: + } + } + + // Process memory variable. + t := dm.defsFor(&memVar) + dm.Insert(t, b, ssa.AdjustBefore) // define memory at entry block + dm.Use(t, po[0]) // Explicitly use memory at last block + + // Next load the map w/ basic definitions for ONames recorded per-block + // Iterate over po to avoid unreachable blocks. + for i := len(po) - 1; i >= 0; i-- { + b := po[i] + m := s.defvars[b.ID] + for n := range m { // no specified order, but per-node trees are independent. + t := dm.defsFor(n) + dm.Insert(t, b, ssa.AdjustWithin) + } + } + + // Find last use of each variable + for _, v := range s.fwdRefs { + b := v.Block + name := v.Aux.(*Node) + t := dm.defsFor(name) + dm.Use(t, b) + } + + for _, t := range trees { + // iterating over names in the outer loop + for change := true; change; { + change = false + for i := t.firstdef; i >= t.lastuse; i-- { + // Iterating in reverse of post-order reduces number of 'change' iterations; + // all possible forward flow goes through each time. + b := po[i] + // Within tree t, would a use at b require a phi function to ensure a single definition? + // TODO: perhaps more efficient to record specific use sites instead of range? + if len(b.Preds) < 2 { + continue // no phi possible + } + phi := t.stm.Find(b, ssa.AdjustWithin, helper) // Look for defs in earlier block or AdjustBefore in this one. + if phi != nil && phi.(*ssa.Block) == b { + continue // has a phi already in this block. + } + var defseen interface{} + // Do preds see different definitions? if so, need a phi function. + for _, e := range b.Preds { + p := e.Block() + dm.Use(t, p) // always count phi pred as "use"; no-op except for loop edges, which matter. + x := t.stm.Find(p, ssa.AdjustAfter, helper) // Look for defs reaching or within predecessors. + if defseen == nil { + defseen = x + } + if defseen != x || x == nil { // TODO: too conservative at loops, does better if x == nil -> continue + // Need to insert a phi function here because predecessors's definitions differ. + change = true + // Phi insertion is at AdjustBefore, visible with find in same block at AdjustWithin or AdjustAfter. + dm.Insert(t, b, ssa.AdjustBefore) + break + } + } + } + } + } + return dm +} + +// FindBetterDefiningBlock tries to find a better block for a definition of OName name +// reaching (or within) p than p itself. If it cannot, it returns p instead. +// This aids in more efficient location of phi functions, since it can skip over +// branch code that might contain a definition of name if it actually does not. +func (m *sparseDefState) FindBetterDefiningBlock(name *Node, p *ssa.Block) *ssa.Block { + if m == nil { + return p + } + t := m.defmapForOname[name] + // For now this is fail-soft, since the old algorithm still works using the unimproved block. + if t == nil { + return p + } + x := t.stm.Find(p, ssa.AdjustAfter, m.helper) + if x == nil { + return p + } + b := x.(*ssa.Block) + if b == nil { + return p + } + return b +} + +func (d *onameDefs) String() string { + return fmt.Sprintf("onameDefs:first=%d,last=%d,tree=%s", d.firstdef, d.lastuse, d.stm.String()) +} diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index e824b476e1a9ab..ecff7c07dea593 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -161,23 +161,19 @@ func buildssa(fn *Node) *ssa.Func { // the function. s.returns = append(s.returns, n) } - case PAUTO | PHEAP: - // TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition - aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n}) - s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) - case PPARAM | PHEAP, PPARAMOUT | PHEAP: - // This ends up wrong, have to do it at the PARAM node instead. + if n.Class == PPARAM && s.canSSA(n) && n.Type.IsPtrShaped() { + s.ptrargs = append(s.ptrargs, n) + n.SetNotLiveAtEnd(true) // SSA takes care of this explicitly + } case PAUTO: // processed at each use, to prevent Addr coming // before the decl. + case PAUTOHEAP: + // moved to heap - already handled by frontend case PFUNC: // local function - already handled by frontend default: - str := "" - if n.Class&PHEAP != 0 { - str = ",heap" - } - s.Unimplementedf("local variable with class %s%s unimplemented", classnames[n.Class&^PHEAP], str) + s.Unimplementedf("local variable with class %s unimplemented", classnames[n.Class]) } } @@ -218,8 +214,16 @@ func buildssa(fn *Node) *ssa.Func { return nil } + prelinkNumvars := s.f.NumValues() + sparseDefState := s.locatePotentialPhiFunctions(fn) + // Link up variable uses to variable definitions - s.linkForwardReferences() + s.linkForwardReferences(sparseDefState) + + if ssa.BuildStats > 0 { + s.f.LogStat("build", s.f.NumBlocks(), "blocks", prelinkNumvars, "vars_before", + s.f.NumValues(), "vars_after", prelinkNumvars*s.f.NumBlocks(), "ssa_phi_loc_cutoff_score") + } // Don't carry reference this around longer than necessary s.exitCode = Nodes{} @@ -282,9 +286,13 @@ type state struct { // list of FwdRef values. fwdRefs []*ssa.Value - // list of PPARAMOUT (return) variables. Does not include PPARAM|PHEAP vars. + // list of PPARAMOUT (return) variables. returns []*Node + // list of PPARAM SSA-able pointer-shaped args. We ensure these are live + // throughout the function to help users avoid premature finalizers. + ptrargs []*Node + cgoUnsafeArgs bool noWB bool WBLineno int32 // line number of first write barrier. 0=no write barriers @@ -577,24 +585,9 @@ func (s *state) stmt(n *Node) { return case ODCL: - if n.Left.Class&PHEAP == 0 { - return - } - if compiling_runtime { - Fatalf("%v escapes to heap, not allowed in runtime.", n) - } - - // TODO: the old pass hides the details of PHEAP - // variables behind ONAME nodes. Figure out if it's better - // to rewrite the tree and make the heapaddr construct explicit - // or to keep this detail hidden behind the scenes. - palloc := prealloc[n.Left] - if palloc == nil { - palloc = callnew(n.Left.Type) - prealloc[n.Left] = palloc + if n.Left.Class == PAUTOHEAP { + Fatalf("DCL %v", n) } - r := s.expr(palloc) - s.assign(n.Left.Name.Heapaddr, r, false, false, n.Lineno, 0) case OLABEL: sym := n.Left.Sym @@ -980,8 +973,7 @@ func (s *state) exit() *ssa.Block { // Store SSAable PPARAMOUT variables back to stack locations. for _, n := range s.returns { - aux := &ssa.ArgSymbol{Typ: n.Type, Node: n} - addr := s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) + addr := s.decladdrs[n] val := s.variable(n, n.Type) s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, n, s.mem()) s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, n.Type.Size(), addr, val, s.mem()) @@ -990,6 +982,16 @@ func (s *state) exit() *ssa.Block { // currently. } + // Keep input pointer args live until the return. This is a bandaid + // fix for 1.7 for what will become in 1.8 explicit runtime.KeepAlive calls. + // For <= 1.7 we guarantee that pointer input arguments live to the end of + // the function to prevent premature (from the user's point of view) + // execution of finalizers. See issue 15277. + // TODO: remove for 1.8? + for _, n := range s.ptrargs { + s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem()) + } + // Do actual return. m := s.mem() b := s.endBlock() @@ -1428,9 +1430,6 @@ func (s *state) expr(n *Node) *ssa.Value { case OCFUNC: aux := s.lookupSymbol(n, &ssa.ExternSymbol{Typ: n.Type, Sym: n.Left.Sym}) return s.entryNewValue1A(ssa.OpAddr, n.Type, aux, s.sb) - case OPARAM: - addr := s.addr(n, false) - return s.newValue2(ssa.OpLoad, n.Left.Type, addr, s.mem()) case ONAME: if n.Class == PFUNC { // "value" of a function is the address of the function's closure @@ -2644,6 +2643,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value { // Start exit block, find address of result. s.startBlock(bNext) + // Keep input pointer args live across calls. This is a bandaid until 1.8. + for _, n := range s.ptrargs { + s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem()) + } res := n.Left.Type.Results() if res.NumFields() == 0 || k != callNormal { // call has no return value. Continue with the next statement. @@ -2724,10 +2727,8 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { // that cse works on their addresses aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n}) return s.newValue1A(ssa.OpAddr, t, aux, s.sp) - case PAUTO | PHEAP, PPARAM | PHEAP, PPARAMOUT | PHEAP, PPARAMREF: - return s.expr(n.Name.Heapaddr) default: - s.Unimplementedf("variable address class %v not implemented", n.Class) + s.Unimplementedf("variable address class %v not implemented", classnames[n.Class]) return nil } case OINDREG: @@ -2770,17 +2771,6 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value { case OCLOSUREVAR: return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, s.entryNewValue0(ssa.OpGetClosurePtr, Ptrto(Types[TUINT8]))) - case OPARAM: - p := n.Left - if p.Op != ONAME || !(p.Class == PPARAM|PHEAP || p.Class == PPARAMOUT|PHEAP) { - s.Fatalf("OPARAM not of ONAME,{PPARAM,PPARAMOUT}|PHEAP, instead %s", nodedump(p, 0)) - } - - // Recover original offset to address passed-in param value. - original_p := *p - original_p.Xoffset = n.Xoffset - aux := &ssa.ArgSymbol{Typ: n.Type, Node: &original_p} - return s.entryNewValue1A(ssa.OpAddr, t, aux, s.sp) case OCONVNOP: addr := s.addr(n.Left, bounded) return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type @@ -2808,12 +2798,14 @@ func (s *state) canSSA(n *Node) bool { if n.Addrtaken { return false } - if n.Class&PHEAP != 0 { + if n.isParamHeapCopy() { return false } + if n.Class == PAUTOHEAP { + Fatalf("canSSA of PAUTOHEAP %v", n) + } switch n.Class { - case PEXTERN, PPARAMREF: - // TODO: maybe treat PPARAMREF with an Arg-like op to read from closure? + case PEXTERN: return false case PPARAMOUT: if hasdefer { @@ -2993,6 +2985,11 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val b.AddEdgeTo(bNext) s.startBlock(bNext) + // Keep input pointer args live across calls. This is a bandaid until 1.8. + for _, n := range s.ptrargs { + s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem()) + } + // Load results res := make([]*ssa.Value, len(results)) for i, t := range results { @@ -3743,7 +3740,8 @@ func (s *state) mem() *ssa.Value { return s.variable(&memVar, ssa.TypeMem) } -func (s *state) linkForwardReferences() { +func (s *state) linkForwardReferences(dm *sparseDefState) { + // Build SSA graph. Each variable on its first use in a basic block // leaves a FwdRef in that block representing the incoming value // of that variable. This function links that ref up with possible definitions, @@ -3758,13 +3756,13 @@ func (s *state) linkForwardReferences() { for len(s.fwdRefs) > 0 { v := s.fwdRefs[len(s.fwdRefs)-1] s.fwdRefs = s.fwdRefs[:len(s.fwdRefs)-1] - s.resolveFwdRef(v) + s.resolveFwdRef(v, dm) } } // resolveFwdRef modifies v to be the variable's value at the start of its block. // v must be a FwdRef op. -func (s *state) resolveFwdRef(v *ssa.Value) { +func (s *state) resolveFwdRef(v *ssa.Value, dm *sparseDefState) { b := v.Block name := v.Aux.(*Node) v.Aux = nil @@ -3803,6 +3801,7 @@ func (s *state) resolveFwdRef(v *ssa.Value) { args := argstore[:0] for _, e := range b.Preds { p := e.Block() + p = dm.FindBetterDefiningBlock(name, p) // try sparse improvement on p args = append(args, s.lookupVarOutgoing(p, v.Type, name, v.Line)) } diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go index 8a233eafe0d6d7..c89917df88c268 100644 --- a/src/cmd/compile/internal/gc/ssa_test.go +++ b/src/cmd/compile/internal/gc/ssa_test.go @@ -57,12 +57,7 @@ func TestArithmetic(t *testing.T) { } // TestFP tests that both backends have the same result for floating point expressions. -func TestFP(t *testing.T) { - if runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le" { - t.Skip("legacy mips64 compiler doesn't handle uint->float conversion correctly (issue 15552)") - } - runTest(t, "fp_ssa.go") -} +func TestFP(t *testing.T) { runTest(t, "fp_ssa.go") } // TestArithmeticBoundary tests boundary results for arithmetic operations. func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary_ssa.go") } diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 3ce8bd16d2d5eb..c2abff7b63487b 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -87,46 +87,53 @@ func linestr(line int32) string { return Ctxt.Line(int(line)) } -func yyerrorl(line int32, format string, args ...interface{}) { - adderr(line, format, args...) - - hcrash() - nerrors++ - if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { - Flusherrors() - fmt.Printf("%v: too many errors\n", linestr(line)) - errorexit() - } +// lasterror keeps track of the most recently issued error. +// It is used to avoid multiple error messages on the same +// line. +var lasterror struct { + syntax int32 // line of last syntax error + other int32 // line of last non-syntax error + msg string // error message of last non-syntax error } -var yyerror_lastsyntax int32 - -func Yyerror(format string, args ...interface{}) { +func yyerrorl(line int32, format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) + if strings.HasPrefix(msg, "syntax error") { nsyntaxerrors++ - - // only one syntax error per line - if yyerror_lastsyntax == lineno { + // only one syntax error per line, no matter what error + if lasterror.syntax == line { return } - yyerror_lastsyntax = lineno - - yyerrorl(lineno, "%s", msg) - return + lasterror.syntax = line + } else { + // only one of multiple equal non-syntax errors per line + // (Flusherrors shows only one of them, so we filter them + // here as best as we can (they may not appear in order) + // so that we don't count them here and exit early, and + // then have nothing to show for.) + if lasterror.other == line && lasterror.msg == msg { + return + } + lasterror.other = line + lasterror.msg = msg } - adderr(lineno, "%s", msg) + adderr(line, "%s", msg) hcrash() nerrors++ if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { Flusherrors() - fmt.Printf("%v: too many errors\n", linestr(lineno)) + fmt.Printf("%v: too many errors\n", linestr(line)) errorexit() } } +func Yyerror(format string, args ...interface{}) { + yyerrorl(lineno, format, args...) +} + func Warn(fmt_ string, args ...interface{}) { adderr(lineno, fmt_, args...) @@ -1224,7 +1231,7 @@ func ullmancalc(n *Node) { switch n.Op { case OREGISTER, OLITERAL, ONAME: ul = 1 - if n.Class == PPARAMREF || (n.Class&PHEAP != 0) { + if n.Class == PAUTOHEAP { ul++ } goto out @@ -2250,6 +2257,7 @@ func isbadimport(path string) bool { } func checknil(x *Node, init *Nodes) { + x = walkexpr(x, nil) // caller has not done this yet if x.Type.IsInterface() { x = Nod(OITAB, x, nil) x = typecheck(x, Erv) diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 8a675ac1579881..e673db9004804c 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -68,11 +68,48 @@ type Node struct { Used bool Isddd bool // is the argument variadic Implicit bool - Addrtaken bool // address taken, even if not moved to heap - Assigned bool // is the variable ever assigned to - Likely int8 // likeliness of if statement - Hasbreak bool // has break statement - hasVal int8 // +1 for Val, -1 for Opt, 0 for not yet set + Addrtaken bool // address taken, even if not moved to heap + Assigned bool // is the variable ever assigned to + Likely int8 // likeliness of if statement + hasVal int8 // +1 for Val, -1 for Opt, 0 for not yet set + flags uint8 // TODO: store more bool fields in this flag field +} + +const ( + hasBreak = 1 << iota + notLiveAtEnd + isClosureVar +) + +func (n *Node) HasBreak() bool { + return n.flags&hasBreak != 0 +} +func (n *Node) SetHasBreak(b bool) { + if b { + n.flags |= hasBreak + } else { + n.flags &^= hasBreak + } +} +func (n *Node) NotLiveAtEnd() bool { + return n.flags¬LiveAtEnd != 0 +} +func (n *Node) SetNotLiveAtEnd(b bool) { + if b { + n.flags |= notLiveAtEnd + } else { + n.flags &^= notLiveAtEnd + } +} +func (n *Node) isClosureVar() bool { + return n.flags&isClosureVar != 0 +} +func (n *Node) setIsClosureVar(b bool) { + if b { + n.flags |= isClosureVar + } else { + n.flags &^= isClosureVar + } } // Val returns the Val for the node. @@ -117,18 +154,18 @@ func (n *Node) SetOpt(x interface{}) { n.E = x } -// Name holds Node fields used only by named nodes (ONAME, OPACK, some OLITERAL). +// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, ODCLFIELD, some OLITERAL). type Name struct { - Pack *Node // real package for import . names - Pkg *Pkg // pkg for OPACK nodes - Heapaddr *Node // temp holding heap address of param - Inlvar *Node // ONAME substitute while inlining - Defn *Node // initializing assignment - Curfn *Node // function for local variables - Param *Param - Decldepth int32 // declaration loop depth, increased for every loop or label - Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one. - Iota int32 // value if this name is iota + Pack *Node // real package for import . names + Pkg *Pkg // pkg for OPACK nodes + Heapaddr *Node // temp holding heap address of param (could move to Param?) + Inlvar *Node // ONAME substitute while inlining (could move to Param?) + Defn *Node // initializing assignment + Curfn *Node // function for local variables + Param *Param // additional fields for ONAME, ODCLFIELD + Decldepth int32 // declaration loop depth, increased for every loop or label + Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one. + Iota int32 // value if this name is iota Funcdepth int32 Method bool // OCALLMETH name Readonly bool @@ -141,16 +178,83 @@ type Name struct { type Param struct { Ntype *Node - // ONAME func param with PHEAP - Outerexpr *Node // expression copied into closure for variable - Stackparam *Node // OPARAM node referring to stack copy of param + // ONAME PAUTOHEAP + Stackcopy *Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only) // ONAME PPARAM Field *Field // TFIELD in arg struct - // ONAME closure param with PPARAMREF - Outer *Node // outer PPARAMREF in nested closure - Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF + // ONAME closure linkage + // Consider: + // + // func f() { + // x := 1 // x1 + // func() { + // use(x) // x2 + // func() { + // use(x) // x3 + // --- parser is here --- + // }() + // }() + // } + // + // There is an original declaration of x and then a chain of mentions of x + // leading into the current function. Each time x is mentioned in a new closure, + // we create a variable representing x for use in that specific closure, + // since the way you get to x is different in each closure. + // + // Let's number the specific variables as shown in the code: + // x1 is the original x, x2 is when mentioned in the closure, + // and x3 is when mentioned in the closure in the closure. + // + // We keep these linked (assume N > 1): + // + // - x1.Defn = original declaration statement for x (like most variables) + // - x1.Innermost = current innermost closure x (in this case x3), or nil for none + // - x1.isClosureVar() = false + // + // - xN.Defn = x1, N > 1 + // - xN.isClosureVar() = true, N > 1 + // - x2.Outer = nil + // - xN.Outer = x(N-1), N > 2 + // + // + // When we look up x in the symbol table, we always get x1. + // Then we can use x1.Innermost (if not nil) to get the x + // for the innermost known closure function, + // but the first reference in a closure will find either no x1.Innermost + // or an x1.Innermost with .Funcdepth < Funcdepth. + // In that case, a new xN must be created, linked in with: + // + // xN.Defn = x1 + // xN.Outer = x1.Innermost + // x1.Innermost = xN + // + // When we finish the function, we'll process its closure variables + // and find xN and pop it off the list using: + // + // x1 := xN.Defn + // x1.Innermost = xN.Outer + // + // We leave xN.Innermost set so that we can still get to the original + // variable quickly. Not shown here, but once we're + // done parsing a function and no longer need xN.Outer for the + // lexical x reference links as described above, closurebody + // recomputes xN.Outer as the semantic x reference link tree, + // even filling in x in intermediate closures that might not + // have mentioned it along the way to inner closures that did. + // See closurebody for details. + // + // During the eventual compilation, then, for closure variables we have: + // + // xN.Defn = original variable + // xN.Outer = variable captured in next outward scope + // to make closure where xN appears + // + // Because of the sharding of pieces of the node, x.Defn means x.Name.Defn + // and x.Innermost/Outer means x.Name.Param.Innermost/Outer. + Innermost *Node + Outer *Node } // Func holds Node fields used only with function-like nodes. @@ -162,9 +266,8 @@ type Func struct { Dcl []*Node // autodcl for this func/closure Inldcl Nodes // copy of dcl for use in inlining Closgen int - Outerfunc *Node + Outerfunc *Node // outer function (for closure) FieldTrack map[*Sym]struct{} - Outer *Node // outer func for closure Ntype *Node // signature Top int // top context (Ecall, Eproc, etc) Closure *Node // OCLOSURE <-> ODCLFUNC @@ -266,7 +369,7 @@ const ( OINDEX // Left[Right] (index of array or slice) OINDEXMAP // Left[Right] (index of map) OKEY // Left:Right (key:value in struct/array/map literal, or slice index pair) - OPARAM // variant of ONAME for on-stack copy of a parameter or return value that escapes. + _ // was OPARAM, but cannot remove without breaking binary blob in builtin.go OLEN // len(Left) OMAKE // make(List) (before type checking converts to one of the following) OMAKECHAN // make(Type, Left) (type is chan) diff --git a/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go b/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go new file mode 100644 index 00000000000000..eea6165938e81f --- /dev/null +++ b/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go @@ -0,0 +1,224 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This program generates a test to verify that the standard arithmetic +// operators properly handle constant folding. The test file should be +// generated with a known working version of go. +// launch with `go run constFoldGen.go` a file called constFold_test.go +// will be written into the grandparent directory containing the tests. + +package main + +import ( + "bytes" + "fmt" + "go/format" + "io/ioutil" + "log" +) + +type op struct { + name, symbol string +} +type szD struct { + name string + sn string + u []uint64 + i []int64 +} + +var szs []szD = []szD{ + szD{name: "uint64", sn: "64", u: []uint64{0, 1, 4294967296, 0xffffFFFFffffFFFF}}, + szD{name: "int64", sn: "64", i: []int64{-0x8000000000000000, -0x7FFFFFFFFFFFFFFF, + -4294967296, -1, 0, 1, 4294967296, 0x7FFFFFFFFFFFFFFE, 0x7FFFFFFFFFFFFFFF}}, + + szD{name: "uint32", sn: "32", u: []uint64{0, 1, 4294967295}}, + szD{name: "int32", sn: "32", i: []int64{-0x80000000, -0x7FFFFFFF, -1, 0, + 1, 0x7FFFFFFF}}, + + szD{name: "uint16", sn: "16", u: []uint64{0, 1, 65535}}, + szD{name: "int16", sn: "16", i: []int64{-32768, -32767, -1, 0, 1, 32766, 32767}}, + + szD{name: "uint8", sn: "8", u: []uint64{0, 1, 255}}, + szD{name: "int8", sn: "8", i: []int64{-128, -127, -1, 0, 1, 126, 127}}, +} + +var ops = []op{ + op{"add", "+"}, op{"sub", "-"}, op{"div", "/"}, op{"mul", "*"}, + op{"lsh", "<<"}, op{"rsh", ">>"}, op{"mod", "%"}, +} + +// compute the result of i op j, cast as type t. +func ansU(i, j uint64, t, op string) string { + var ans uint64 + switch op { + case "+": + ans = i + j + case "-": + ans = i - j + case "*": + ans = i * j + case "/": + if j != 0 { + ans = i / j + } + case "%": + if j != 0 { + ans = i % j + } + case "<<": + ans = i << j + case ">>": + ans = i >> j + } + switch t { + case "uint32": + ans = uint64(uint32(ans)) + case "uint16": + ans = uint64(uint16(ans)) + case "uint8": + ans = uint64(uint8(ans)) + } + return fmt.Sprintf("%d", ans) +} + +// compute the result of i op j, cast as type t. +func ansS(i, j int64, t, op string) string { + var ans int64 + switch op { + case "+": + ans = i + j + case "-": + ans = i - j + case "*": + ans = i * j + case "/": + if j != 0 { + ans = i / j + } + case "%": + if j != 0 { + ans = i % j + } + case "<<": + ans = i << uint64(j) + case ">>": + ans = i >> uint64(j) + } + switch t { + case "int32": + ans = int64(int32(ans)) + case "int16": + ans = int64(int16(ans)) + case "int8": + ans = int64(int8(ans)) + } + return fmt.Sprintf("%d", ans) +} + +func main() { + + w := new(bytes.Buffer) + + fmt.Fprintf(w, "package gc\n") + fmt.Fprintf(w, "import \"testing\"\n") + + for _, s := range szs { + for _, o := range ops { + if o.symbol == "<<" || o.symbol == ">>" { + // shifts handled separately below, as they can have + // different types on the LHS and RHS. + continue + } + fmt.Fprintf(w, "func TestConstFold%s%s(t *testing.T) {\n", s.name, o.name) + fmt.Fprintf(w, "\tvar x, y, r %s\n", s.name) + // unsigned test cases + for _, c := range s.u { + fmt.Fprintf(w, "\tx = %d\n", c) + for _, d := range s.u { + if d == 0 && (o.symbol == "/" || o.symbol == "%") { + continue + } + fmt.Fprintf(w, "\ty = %d\n", d) + fmt.Fprintf(w, "\tr = x %s y\n", o.symbol) + want := ansU(c, d, s.name, o.symbol) + fmt.Fprintf(w, "\tif r != %s {\n", want) + fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want) + fmt.Fprintf(w, "\t}\n") + } + } + // signed test cases + for _, c := range s.i { + fmt.Fprintf(w, "\tx = %d\n", c) + for _, d := range s.i { + if d == 0 && (o.symbol == "/" || o.symbol == "%") { + continue + } + fmt.Fprintf(w, "\ty = %d\n", d) + fmt.Fprintf(w, "\tr = x %s y\n", o.symbol) + want := ansS(c, d, s.name, o.symbol) + fmt.Fprintf(w, "\tif r != %s {\n", want) + fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want) + fmt.Fprintf(w, "\t}\n") + } + } + fmt.Fprintf(w, "}\n") + } + } + + // Special signed/unsigned cases for shifts + for _, ls := range szs { + for _, rs := range szs { + if rs.name[0] != 'u' { + continue + } + for _, o := range ops { + if o.symbol != "<<" && o.symbol != ">>" { + continue + } + fmt.Fprintf(w, "func TestConstFold%s%s%s(t *testing.T) {\n", ls.name, rs.name, o.name) + fmt.Fprintf(w, "\tvar x, r %s\n", ls.name) + fmt.Fprintf(w, "\tvar y %s\n", rs.name) + // unsigned LHS + for _, c := range ls.u { + fmt.Fprintf(w, "\tx = %d\n", c) + for _, d := range rs.u { + fmt.Fprintf(w, "\ty = %d\n", d) + fmt.Fprintf(w, "\tr = x %s y\n", o.symbol) + want := ansU(c, d, ls.name, o.symbol) + fmt.Fprintf(w, "\tif r != %s {\n", want) + fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want) + fmt.Fprintf(w, "\t}\n") + } + } + // signed LHS + for _, c := range ls.i { + fmt.Fprintf(w, "\tx = %d\n", c) + for _, d := range rs.u { + fmt.Fprintf(w, "\ty = %d\n", d) + fmt.Fprintf(w, "\tr = x %s y\n", o.symbol) + want := ansS(c, int64(d), ls.name, o.symbol) + fmt.Fprintf(w, "\tif r != %s {\n", want) + fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want) + fmt.Fprintf(w, "\t}\n") + } + } + fmt.Fprintf(w, "}\n") + } + } + } + // gofmt result + b := w.Bytes() + src, err := format.Source(b) + if err != nil { + fmt.Printf("%s\n", b) + panic(err) + } + + // write to file + err = ioutil.WriteFile("../../constFold_test.go", src, 0666) + if err != nil { + log.Fatalf("can't write output: %v\n", err) + } +} diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index 9ae05f7ff1f1a5..ab13df6eba5981 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -223,10 +223,20 @@ type StructType struct { // Map links such structs back to their map type. Map *Type - Funarg bool // whether this struct represents function parameters - Haspointers uint8 // 0 unknown, 1 no, 2 yes + Funarg Funarg // type of function arguments for arg struct + Haspointers uint8 // 0 unknown, 1 no, 2 yes } +// Fnstruct records the kind of function argument +type Funarg uint8 + +const ( + FunargNone Funarg = iota + FunargRcvr // receiver + FunargParams // input parameters + FunargResults // output results +) + // StructType returns t's extra struct-specific fields. func (t *Type) StructType() *StructType { t.wantEtype(TSTRUCT) @@ -287,7 +297,7 @@ type SliceType struct { type Field struct { Nointerface bool Embedded uint8 // embedded field - Funarg bool + Funarg Funarg Broke bool // broken field definition Isddd bool // field is ... argument @@ -786,7 +796,7 @@ func (t *Type) SetNname(n *Node) { // IsFuncArgStruct reports whether t is a struct representing function parameters. func (t *Type) IsFuncArgStruct() bool { - return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg + return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg != FunargNone } func (t *Type) Methods() *Fields { diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 7fccbe1a52b945..c8ee9417e611f6 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -796,8 +796,8 @@ OpSwitch: var l *Node for l = n.Left; l != r; l = l.Left { l.Addrtaken = true - if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil { - l.Name.Param.Closure.Addrtaken = true + if l.isClosureVar() { + l.Name.Defn.Addrtaken = true } } @@ -805,8 +805,8 @@ OpSwitch: Fatalf("found non-orig name node %v", l) } l.Addrtaken = true - if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil { - l.Name.Param.Closure.Addrtaken = true + if l.isClosureVar() { + l.Name.Defn.Addrtaken = true } n.Left = defaultlit(n.Left, nil) l = n.Left @@ -3099,7 +3099,7 @@ func islvalue(n *Node) bool { return false } fallthrough - case OIND, ODOTPTR, OCLOSUREVAR, OPARAM: + case OIND, ODOTPTR, OCLOSUREVAR: return true case ODOT: @@ -3128,14 +3128,14 @@ func checkassign(stmt *Node, n *Node) { var l *Node for l = n; l != r; l = l.Left { l.Assigned = true - if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil { - l.Name.Param.Closure.Assigned = true + if l.isClosureVar() { + l.Name.Defn.Assigned = true } } l.Assigned = true - if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil { - l.Name.Param.Closure.Assigned = true + if l.isClosureVar() { + l.Name.Defn.Assigned = true } } @@ -3153,7 +3153,7 @@ func checkassign(stmt *Node, n *Node) { } if n.Op == ODOT && n.Left.Op == OINDEXMAP { - Yyerror("cannot directly assign to struct field %v in map", n) + Yyerror("cannot assign to struct field %v in map", n) return } @@ -3786,12 +3786,12 @@ func markbreak(n *Node, implicit *Node) { case OBREAK: if n.Left == nil { if implicit != nil { - implicit.Hasbreak = true + implicit.SetHasBreak(true) } } else { lab := n.Left.Sym.Label if lab != nil { - lab.Def.Hasbreak = true + lab.Def.SetHasBreak(true) } } @@ -3867,7 +3867,7 @@ func (n *Node) isterminating() bool { if n.Left != nil { return false } - if n.Hasbreak { + if n.HasBreak() { return false } return true @@ -3876,7 +3876,7 @@ func (n *Node) isterminating() bool { return n.Nbody.isterminating() && n.Rlist.isterminating() case OSWITCH, OTYPESW, OSELECT: - if n.Hasbreak { + if n.HasBreak() { return false } def := 0 diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go index 84df22502fff93..b55af7e25a0a7b 100644 --- a/src/cmd/compile/internal/gc/universe.go +++ b/src/cmd/compile/internal/gc/universe.go @@ -362,16 +362,16 @@ func lexinit1() { // t = interface { Error() string } rcvr := typ(TSTRUCT) - rcvr.StructType().Funarg = true + rcvr.StructType().Funarg = FunargRcvr field := newField() field.Type = Ptrto(typ(TSTRUCT)) rcvr.SetFields([]*Field{field}) in := typ(TSTRUCT) - in.StructType().Funarg = true + in.StructType().Funarg = FunargParams out := typ(TSTRUCT) - out.StructType().Funarg = true + out.StructType().Funarg = FunargResults field = newField() field.Type = Types[TSTRING] out.SetFields([]*Field{field}) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 14784e284e93da..66eb7e97ac6c6d 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -27,9 +27,8 @@ func walk(fn *Node) { lno := lineno // Final typecheck for any unused variables. - // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. for i, ln := range fn.Func.Dcl { - if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO { + if ln.Op == ONAME && (ln.Class == PAUTO || ln.Class == PAUTOHEAP) { ln = typecheck(ln, Erv|Easgn) fn.Func.Dcl[i] = ln } @@ -37,13 +36,13 @@ func walk(fn *Node) { // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. for _, ln := range fn.Func.Dcl { - if ln.Op == ONAME && ln.Class&^PHEAP == PAUTO && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used { + if ln.Op == ONAME && (ln.Class == PAUTO || ln.Class == PAUTOHEAP) && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Used { ln.Name.Defn.Left.Used = true } } for _, ln := range fn.Func.Dcl { - if ln.Op != ONAME || ln.Class&^PHEAP != PAUTO || ln.Sym.Name[0] == '&' || ln.Used { + if ln.Op != ONAME || (ln.Class != PAUTO && ln.Class != PAUTOHEAP) || ln.Sym.Name[0] == '&' || ln.Used { continue } if defn := ln.Name.Defn; defn != nil && defn.Op == OTYPESW { @@ -97,13 +96,13 @@ func samelist(a, b []*Node) bool { func paramoutheap(fn *Node) bool { for _, ln := range fn.Func.Dcl { switch ln.Class { - case PPARAMOUT, - PPARAMOUT | PHEAP: - return ln.Addrtaken + case PPARAMOUT: + if ln.isParamStackCopy() || ln.Addrtaken { + return true + } + case PAUTO: // stop early - parameters are over - case PAUTO, - PAUTO | PHEAP: return false } } @@ -212,7 +211,6 @@ func walkstmt(n *Node) *Node { n = addinit(n, init.Slice()) case OBREAK, - ODCL, OCONTINUE, OFALL, OGOTO, @@ -224,6 +222,21 @@ func walkstmt(n *Node) *Node { OVARLIVE: break + case ODCL: + v := n.Left + if v.Class == PAUTOHEAP { + if compiling_runtime { + Yyerror("%v escapes to heap, not allowed in runtime.", v) + } + if prealloc[v] == nil { + prealloc[v] = callnew(v.Type) + } + nn := Nod(OAS, v.Name.Heapaddr, prealloc[v]) + nn.Colas = true + nn = typecheck(nn, Etop) + return walkstmt(nn) + } + case OBLOCK: walkstmtlist(n.List.Slice()) @@ -295,11 +308,14 @@ func walkstmt(n *Node) *Node { var cl Class for _, ln := range Curfn.Func.Dcl { - cl = ln.Class &^ PHEAP - if cl == PAUTO { + cl = ln.Class + if cl == PAUTO || cl == PAUTOHEAP { break } if cl == PPARAMOUT { + if ln.isParamStackCopy() { + ln = walkexpr(typecheck(Nod(OIND, ln.Name.Heapaddr, nil), Erv), nil) + } rl = append(rl, ln) } } @@ -487,6 +503,12 @@ func walkexpr(n *Node, init *Nodes) *Node { Fatalf("missed typecheck: %v\n", Nconv(n, FmtSign)) } + if n.Op == ONAME && n.Class == PAUTOHEAP { + nn := Nod(OIND, n.Name.Heapaddr, nil) + nn = typecheck(nn, Erv) + return walkexpr(nn, init) + } + opswitch: switch n.Op { default: @@ -497,7 +519,6 @@ opswitch: ONONAME, OINDREG, OEMPTY, - OPARAM, OGETG: case ONOT, @@ -626,9 +647,7 @@ opswitch: n.Addable = true case ONAME: - if n.Class&PHEAP == 0 && n.Class != PPARAMREF { - n.Addable = true - } + n.Addable = true case OCALLINTER: usemethod(n) @@ -1640,7 +1659,7 @@ func ascompatee(op Op, nl, nr []*Node, init *Nodes) []*Node { break } // Do not generate 'x = x' during return. See issue 4014. - if op == ORETURN && nl[i] == nr[i] { + if op == ORETURN && samesafeexpr(nl[i], nr[i]) { continue } nn = append(nn, ascompatee1(op, nl[i], nr[i], init)) @@ -2515,7 +2534,7 @@ func vmatch1(l *Node, r *Node) bool { switch l.Op { case ONAME: switch l.Class { - case PPARAM, PPARAMREF, PAUTO: + case PPARAM, PAUTO: break // assignment to non-stack variable @@ -2550,41 +2569,31 @@ func vmatch1(l *Node, r *Node) bool { // and to copy non-result prameters' values from the stack. // If out is true, then code is also produced to zero-initialize their // stack memory addresses. -func paramstoheap(params *Type, out bool) []*Node { +func paramstoheap(params *Type) []*Node { var nn []*Node for _, t := range params.Fields().Slice() { - v := t.Nname - if v != nil && v.Sym != nil && strings.HasPrefix(v.Sym.Name, "~r") { // unnamed result - v = nil - } - // For precise stacks, the garbage collector assumes results // are always live, so zero them always. - if out { + if params.StructType().Funarg == FunargResults { // Defer might stop a panic and show the // return values as they exist at the time of panic. // Make sure to zero them on entry to the function. - nn = append(nn, Nod(OAS, nodarg(t, -1), nil)) + nn = append(nn, Nod(OAS, nodarg(t, 1), nil)) } - if v == nil || v.Class&PHEAP == 0 { + v := t.Nname + if v != nil && v.Sym != nil && strings.HasPrefix(v.Sym.Name, "~r") { // unnamed result + v = nil + } + if v == nil { continue } - // generate allocation & copying code - if compiling_runtime { - Yyerror("%v escapes to heap, not allowed in runtime.", v) - } - if prealloc[v] == nil { - prealloc[v] = callnew(v.Type) - } - nn = append(nn, Nod(OAS, v.Name.Heapaddr, prealloc[v])) - if v.Class&^PHEAP != PPARAMOUT { - as := Nod(OAS, v, v.Name.Param.Stackparam) - v.Name.Param.Stackparam.Typecheck = 1 - as = typecheck(as, Etop) - as = applywritebarrier(as) - nn = append(nn, as) + if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil { + nn = append(nn, walkstmt(Nod(ODCL, v, nil))) + if stackcopy.Class == PPARAM { + nn = append(nn, walkstmt(typecheck(Nod(OAS, v, stackcopy), Etop))) + } } } @@ -2597,10 +2606,12 @@ func returnsfromheap(params *Type) []*Node { var nn []*Node for _, t := range params.Fields().Slice() { v := t.Nname - if v == nil || v.Class != PHEAP|PPARAMOUT { + if v == nil { continue } - nn = append(nn, Nod(OAS, v.Name.Param.Stackparam, v)) + if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil && stackcopy.Class == PPARAMOUT { + nn = append(nn, walkstmt(typecheck(Nod(OAS, stackcopy, v), Etop))) + } } return nn @@ -2612,9 +2623,9 @@ func returnsfromheap(params *Type) []*Node { func heapmoves() { lno := lineno lineno = Curfn.Lineno - nn := paramstoheap(Curfn.Type.Recvs(), false) - nn = append(nn, paramstoheap(Curfn.Type.Params(), false)...) - nn = append(nn, paramstoheap(Curfn.Type.Results(), true)...) + nn := paramstoheap(Curfn.Type.Recvs()) + nn = append(nn, paramstoheap(Curfn.Type.Params())...) + nn = append(nn, paramstoheap(Curfn.Type.Results())...) Curfn.Func.Enter.Append(nn...) lineno = Curfn.Func.Endlineno Curfn.Func.Exit.Append(returnsfromheap(Curfn.Type.Results())...) diff --git a/src/cmd/compile/internal/mips64/gsubr.go b/src/cmd/compile/internal/mips64/gsubr.go index 864fd76d121f9e..eb56d8b82e6e93 100644 --- a/src/cmd/compile/internal/mips64/gsubr.go +++ b/src/cmd/compile/internal/mips64/gsubr.go @@ -466,7 +466,7 @@ func gmove(f *gc.Node, t *gc.Node) { //return; // algorithm is: // if small enough, use native int64 -> float64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. + // otherwise, halve (x -> (x>>1)|(x&1)), convert, and double. /* * integer to float */ @@ -496,9 +496,16 @@ func gmove(f *gc.Node, t *gc.Node) { gmove(&bigi, &rtmp) gins(mips.AAND, &r1, &rtmp) p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0) - p2 := gins(mips.ASRLV, nil, &r1) + var r3 gc.Node + gc.Regalloc(&r3, gc.Types[gc.TUINT64], nil) + p2 := gins3(mips.AAND, nil, &r1, &r3) p2.From.Type = obj.TYPE_CONST p2.From.Offset = 1 + p3 := gins(mips.ASRLV, nil, &r1) + p3.From.Type = obj.TYPE_CONST + p3.From.Offset = 1 + gins(mips.AOR, &r3, &r1) + gc.Regfree(&r3) gc.Patch(p1, gc.Pc) } diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 60be3de5218ad9..bfedd477946b53 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -316,7 +316,7 @@ func checkFunc(f *Func) { } // domCheck reports whether x dominates y (including x==y). -func domCheck(f *Func, sdom sparseTree, x, y *Block) bool { +func domCheck(f *Func, sdom SparseTree, x, y *Block) bool { if !sdom.isAncestorEq(f.Entry, y) { // unreachable - ignore return true diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index bc9c830ee958aa..b3c7544ad129ab 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -86,14 +86,14 @@ func Compile(f *Func) { // Surround timing information w/ enough context to allow comparisons. time := tEnd.Sub(tStart).Nanoseconds() if p.time { - f.logStat("TIME(ns)", time) + f.LogStat("TIME(ns)", time) } if p.mem { var mEnd runtime.MemStats runtime.ReadMemStats(&mEnd) nBytes := mEnd.TotalAlloc - mStart.TotalAlloc nAllocs := mEnd.Mallocs - mStart.Mallocs - f.logStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs) + f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs) } } if checkEnabled { @@ -124,6 +124,10 @@ var checkEnabled = false var IntrinsicsDebug int var IntrinsicsDisable bool +var BuildDebug int +var BuildTest int +var BuildStats int + // PhaseOption sets the specified flag in the specified ssa phase, // returning empty string if this was successful or a string explaining // the error if it was not. @@ -174,6 +178,19 @@ func PhaseOption(phase, flag string, val int) string { } return "" } + if phase == "build" { + switch flag { + case "debug": + BuildDebug = val + case "test": + BuildTest = val + case "stats": + BuildStats = val + default: + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + } + return "" + } underphase := strings.Replace(phase, "_", " ", -1) var re *regexp.Regexp diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 26f16bae580b3d..ddb58d9f79b92c 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -9,23 +9,25 @@ import ( "crypto/sha1" "fmt" "os" + "strconv" "strings" ) type Config struct { - arch string // "amd64", etc. - IntSize int64 // 4 or 8 - PtrSize int64 // 4 or 8 - lowerBlock func(*Block) bool // lowering function - lowerValue func(*Value, *Config) bool // lowering function - registers []Register // machine registers - flagRegMask regMask // flag register mask - fe Frontend // callbacks into compiler frontend - HTML *HTMLWriter // html writer, for debugging - ctxt *obj.Link // Generic arch information - optimize bool // Do optimization - noDuffDevice bool // Don't use Duff's device - curFunc *Func + arch string // "amd64", etc. + IntSize int64 // 4 or 8 + PtrSize int64 // 4 or 8 + lowerBlock func(*Block) bool // lowering function + lowerValue func(*Value, *Config) bool // lowering function + registers []Register // machine registers + flagRegMask regMask // flag register mask + fe Frontend // callbacks into compiler frontend + HTML *HTMLWriter // html writer, for debugging + ctxt *obj.Link // Generic arch information + optimize bool // Do optimization + noDuffDevice bool // Don't use Duff's device + sparsePhiCutoff uint64 // Sparse phi location algorithm used above this #blocks*#variables score + curFunc *Func // TODO: more stuff. Compiler flags of interest, ... @@ -162,10 +164,27 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config c.logfiles = make(map[string]*os.File) + // cutoff is compared with product of numblocks and numvalues, + // if product is smaller than cutoff, use old non-sparse method. + // cutoff == 0 implies all sparse. + // cutoff == -1 implies none sparse. + // Good cutoff values seem to be O(million) depending on constant factor cost of sparse. + // TODO: get this from a flag, not an environment variable + c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash + ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF") + if ev != "" { + v, err := strconv.ParseInt(ev, 10, 64) + if err != nil { + fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev) + } + c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse + } + return c } -func (c *Config) Frontend() Frontend { return c.fe } +func (c *Config) Frontend() Frontend { return c.fe } +func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff } // NewFunc returns a new, empty function object. // Caller must call f.Free() before calling NewFunc again. @@ -262,3 +281,7 @@ func (c *Config) DebugHashMatch(evname, name string) bool { } return false } + +func (c *Config) DebugNameMatch(evname, name string) bool { + return os.Getenv(evname) == name +} diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go index 8cc0db1d17266e..ad4e4161591d0a 100644 --- a/src/cmd/compile/internal/ssa/cse.go +++ b/src/cmd/compile/internal/ssa/cse.go @@ -137,10 +137,9 @@ func cse(f *Func) { // if v and w are in the same equivalence class and v dominates w. rewrite := make([]*Value, f.NumValues()) for _, e := range partition { - sort.Sort(sortbyentry{e, f.sdom}) + sort.Sort(partitionByDom{e, f.sdom}) for i := 0; i < len(e)-1; i++ { - // e is sorted by entry value so maximal dominant element should be - // found first in the slice + // e is sorted by domorder, so a maximal dominant element is first in the slice v := e[i] if v == nil { continue @@ -157,9 +156,7 @@ func cse(f *Func) { rewrite[w.ID] = v e[j] = nil } else { - // since the blocks are assorted in ascending order by entry number - // once we know that we don't dominate a block we can't dominate any - // 'later' block + // e is sorted by domorder, so v.Block doesn't dominate any subsequent blocks in e break } } @@ -190,7 +187,7 @@ func cse(f *Func) { } } if f.pass.stats > 0 { - f.logStat("CSE REWRITES", rewrites) + f.LogStat("CSE REWRITES", rewrites) } } @@ -311,15 +308,15 @@ func (sv sortvalues) Less(i, j int) bool { return v.ID < w.ID } -type sortbyentry struct { +type partitionByDom struct { a []*Value // array of values - sdom sparseTree + sdom SparseTree } -func (sv sortbyentry) Len() int { return len(sv.a) } -func (sv sortbyentry) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] } -func (sv sortbyentry) Less(i, j int) bool { +func (sv partitionByDom) Len() int { return len(sv.a) } +func (sv partitionByDom) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] } +func (sv partitionByDom) Less(i, j int) bool { v := sv.a[i] w := sv.a[j] - return sv.sdom.maxdomorder(v.Block) < sv.sdom.maxdomorder(w.Block) + return sv.sdom.domorder(v.Block) < sv.sdom.domorder(w.Block) } diff --git a/src/cmd/compile/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go index 78ba2e9e1b6aae..0c532c87ff7211 100644 --- a/src/cmd/compile/internal/ssa/dom.go +++ b/src/cmd/compile/internal/ssa/dom.go @@ -20,9 +20,9 @@ const ( // postorder computes a postorder traversal ordering for the // basic blocks in f. Unreachable blocks will not appear. func postorder(f *Func) []*Block { - return postorderWithNumbering(f, []int{}) + return postorderWithNumbering(f, []int32{}) } -func postorderWithNumbering(f *Func, ponums []int) []*Block { +func postorderWithNumbering(f *Func, ponums []int32) []*Block { mark := make([]markKind, f.NumBlocks()) // result ordering @@ -40,7 +40,7 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block { s = s[:len(s)-1] mark[b.ID] = done if len(ponums) > 0 { - ponums[b.ID] = len(order) + ponums[b.ID] = int32(len(order)) } order = append(order, b) case notExplored: diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 2c7a8a1f11e1fc..1d60bb606ad226 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -37,7 +37,7 @@ type Func struct { freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil. idom []*Block // precomputed immediate dominators - sdom sparseTree // precomputed dominator tree + sdom SparseTree // precomputed dominator tree constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type } @@ -104,12 +104,16 @@ func (f *Func) newValue(op Op, t Type, b *Block, line int32) *Value { // context to allow item-by-item comparisons across runs. // For example: // awk 'BEGIN {FS="\t"} $3~/TIME/{sum+=$4} END{print "t(ns)=",sum}' t.log -func (f *Func) logStat(key string, args ...interface{}) { +func (f *Func) LogStat(key string, args ...interface{}) { value := "" for _, a := range args { value += fmt.Sprintf("\t%v", a) } - f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", f.pass.name, key, value, f.Name) + n := "missing_pass" + if f.pass != nil { + n = f.pass.name + } + f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name) } // freeValue frees a value. It must no longer be referenced. diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 8c40c77f06d5cb..94650141949c8b 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -382,9 +382,9 @@ var genericOps = []opData{ {name: "ComplexImag", argLength: 1}, // imag(arg0) // Strings - {name: "StringMake", argLength: 2}, // arg0=ptr, arg1=len - {name: "StringPtr", argLength: 1}, // ptr(arg0) - {name: "StringLen", argLength: 1}, // len(arg0) + {name: "StringMake", argLength: 2}, // arg0=ptr, arg1=len + {name: "StringPtr", argLength: 1, typ: "BytePtr"}, // ptr(arg0) + {name: "StringLen", argLength: 1, typ: "Int"}, // len(arg0) // Interfaces {name: "IMake", argLength: 2}, // arg0=itab, arg1=data @@ -407,7 +407,7 @@ var genericOps = []opData{ {name: "LoadReg", argLength: 1}, // Used during ssa construction. Like Copy, but the arg has not been specified yet. - {name: "FwdRef"}, + {name: "FwdRef", aux: "Sym"}, // Unknown value. Used for Values whose values don't matter because they are dead code. {name: "Unknown"}, @@ -415,6 +415,7 @@ var genericOps = []opData{ {name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem"}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem {name: "VarKill", argLength: 1, aux: "Sym"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem {name: "VarLive", argLength: 1, aux: "Sym"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem + {name: "KeepAlive", argLength: 2, typ: "Mem"}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem } // kind control successors implicit exit diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index fcabdb1dd908d9..0fc5749f1d5aa6 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -150,9 +150,6 @@ func genRules(arch arch) { fmt.Fprintln(w, "// generated with: cd gen; go run *.go") fmt.Fprintln(w) fmt.Fprintln(w, "package ssa") - if *genLog { - fmt.Fprintln(w, "import \"fmt\"") - } fmt.Fprintln(w, "import \"math\"") fmt.Fprintln(w, "var _ = math.MinInt8 // in case not otherwise used") @@ -196,7 +193,7 @@ func genRules(arch arch) { genResult(w, arch, result, rule.loc) if *genLog { - fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc) + fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc) } fmt.Fprintf(w, "return true\n") @@ -300,7 +297,7 @@ func genRules(arch arch) { } if *genLog { - fmt.Fprintf(w, "fmt.Println(\"rewrite %s\")\n", rule.loc) + fmt.Fprintf(w, "logRule(\"%s\")\n", rule.loc) } fmt.Fprintf(w, "return true\n") diff --git a/src/cmd/compile/internal/ssa/likelyadjust.go b/src/cmd/compile/internal/ssa/likelyadjust.go index 3f03943a7418d9..cb2d82f3527640 100644 --- a/src/cmd/compile/internal/ssa/likelyadjust.go +++ b/src/cmd/compile/internal/ssa/likelyadjust.go @@ -32,7 +32,7 @@ type loop struct { } // outerinner records that outer contains inner -func (sdom sparseTree) outerinner(outer, inner *loop) { +func (sdom SparseTree) outerinner(outer, inner *loop) { oldouter := inner.outer if oldouter == nil || sdom.isAncestorEq(oldouter.header, outer.header) { inner.outer = outer @@ -59,7 +59,7 @@ type loopnest struct { f *Func b2l []*loop po []*Block - sdom sparseTree + sdom SparseTree loops []*loop // Record which of the lazily initialized fields have actually been initialized. @@ -238,7 +238,7 @@ func (l *loop) LongString() string { // containing block b; the header must dominate b. loop itself // is assumed to not be that loop. For acceptable performance, // we're relying on loop nests to not be terribly deep. -func (l *loop) nearestOuterLoop(sdom sparseTree, b *Block) *loop { +func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop { var o *loop for o = l.outer; o != nil && !sdom.isAncestorEq(o.header, b); o = o.outer { } @@ -335,7 +335,7 @@ func loopnestfor(f *Func) *loopnest { inner++ } - f.logStat("loopstats:", + f.LogStat("loopstats:", l.depth, "depth", x, "exits", inner, "is_inner", cf, "is_callfree", l.nBlocks, "n_blocks") } diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index af0ee4cccf0729..e271ed4ef6b9ab 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -21,7 +21,7 @@ func checkLower(f *Func) { continue // lowered } switch v.Op { - case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive: + case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive: continue // ok not to lower } s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString() diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 558d041624a131..fc8214b9d9263e 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -674,6 +674,7 @@ const ( OpVarDef OpVarKill OpVarLive + OpKeepAlive ) var opcodeTable = [...]opInfo{ @@ -6167,6 +6168,7 @@ var opcodeTable = [...]opInfo{ }, { name: "FwdRef", + auxType: auxSym, argLen: 0, generic: true, }, @@ -6193,6 +6195,11 @@ var opcodeTable = [...]opInfo{ argLen: 1, generic: true, }, + { + name: "KeepAlive", + argLen: 2, + generic: true, + }, } func (o Op) Asm() obj.As { return opcodeTable[o].asm } diff --git a/src/cmd/compile/internal/ssa/passbm_test.go b/src/cmd/compile/internal/ssa/passbm_test.go index 8dff17a5b413fb..87069abc3b1b9f 100644 --- a/src/cmd/compile/internal/ssa/passbm_test.go +++ b/src/cmd/compile/internal/ssa/passbm_test.go @@ -35,7 +35,7 @@ func benchFnPass(b *testing.B, fn passFunc, size int, bg blockGen) { b.ReportAllocs() c := NewConfig("amd64", DummyFrontend{b}, nil, true) fun := Fun(c, "entry", bg(size)...) - + domTree(fun.f) CheckFunc(fun.f) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -51,7 +51,7 @@ func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) { b.ReportAllocs() c := NewConfig("amd64", DummyFrontend{b}, nil, true) fun := Fun(c, "entry", bg(b.N)...) - + domTree(fun.f) CheckFunc(fun.f) b.ResetTimer() for i := 0; i < passCount; i++ { diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index 17ef5e461a9849..4416fa2cf37698 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -515,7 +515,7 @@ func prove(f *Func) { // getBranch returns the range restrictions added by p // when reaching b. p is the immediate dominator of b. -func getBranch(sdom sparseTree, p *Block, b *Block) branch { +func getBranch(sdom SparseTree, p *Block, b *Block) branch { if p == nil || p.Kind != BlockIf { return unknown } diff --git a/src/cmd/compile/internal/ssa/redblack32.go b/src/cmd/compile/internal/ssa/redblack32.go new file mode 100644 index 00000000000000..ae1ec352e782cd --- /dev/null +++ b/src/cmd/compile/internal/ssa/redblack32.go @@ -0,0 +1,429 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssa + +import "fmt" + +const ( + rankLeaf rbrank = 1 + rankZero rbrank = 0 +) + +type rbrank int8 + +// RBTint32 is a red-black tree with data stored at internal nodes, +// following Tarjan, Data Structures and Network Algorithms, +// pp 48-52, using explicit rank instead of red and black. +// Deletion is not yet implemented because it is not yet needed. +// Extra operations glb, lub, glbEq, lubEq are provided for +// use in sparse lookup algorithms. +type RBTint32 struct { + root *node32 + // An extra-clever implementation will have special cases + // for small sets, but we are not extra-clever today. +} + +func (t *RBTint32) String() string { + if t.root == nil { + return "[]" + } + return "[" + t.root.String() + "]" +} + +func (t *node32) String() string { + s := "" + if t.left != nil { + s = t.left.String() + " " + } + s = s + fmt.Sprintf("k=%d,d=%v", t.key, t.data) + if t.right != nil { + s = s + " " + t.right.String() + } + return s +} + +type node32 struct { + // Standard conventions hold for left = smaller, right = larger + left, right, parent *node32 + data interface{} + key int32 + rank rbrank // From Tarjan pp 48-49: + // If x is a node with a parent, then x.rank <= x.parent.rank <= x.rank+1. + // If x is a node with a grandparent, then x.rank < x.parent.parent.rank. + // If x is an "external [null] node", then x.rank = 0 && x.parent.rank = 1. + // Any node with one or more null children should have rank = 1. +} + +// makeNode returns a new leaf node with the given key and nil data. +func (t *RBTint32) makeNode(key int32) *node32 { + return &node32{key: key, rank: rankLeaf} +} + +// IsEmpty reports whether t is empty. +func (t *RBTint32) IsEmpty() bool { + return t.root == nil +} + +// IsSingle reports whether t is a singleton (leaf). +func (t *RBTint32) IsSingle() bool { + return t.root != nil && t.root.isLeaf() +} + +// VisitInOrder applies f to the key and data pairs in t, +// with keys ordered from smallest to largest. +func (t *RBTint32) VisitInOrder(f func(int32, interface{})) { + if t.root == nil { + return + } + t.root.visitInOrder(f) +} + +func (n *node32) Data() interface{} { + if n == nil { + return nil + } + return n.data +} + +func (n *node32) keyAndData() (k int32, d interface{}) { + if n == nil { + k = 0 + d = nil + } else { + k = n.key + d = n.data + } + return +} + +func (n *node32) Rank() rbrank { + if n == nil { + return 0 + } + return n.rank +} + +// Find returns the data associated with key in the tree, or +// nil if key is not in the tree. +func (t *RBTint32) Find(key int32) interface{} { + return t.root.find(key).Data() +} + +// Insert adds key to the tree and associates key with data. +// If key was already in the tree, it updates the associated data. +// Insert returns the previous data associated with key, +// or nil if key was not present. +// Insert panics if data is nil. +func (t *RBTint32) Insert(key int32, data interface{}) interface{} { + if data == nil { + panic("Cannot insert nil data into tree") + } + n := t.root + var newroot *node32 + if n == nil { + n = t.makeNode(key) + newroot = n + } else { + newroot, n = n.insert(key, t) + } + r := n.data + n.data = data + t.root = newroot + return r +} + +// Min returns the minimum element of t and its associated data. +// If t is empty, then (0, nil) is returned. +func (t *RBTint32) Min() (k int32, d interface{}) { + return t.root.min().keyAndData() +} + +// Max returns the maximum element of t and its associated data. +// If t is empty, then (0, nil) is returned. +func (t *RBTint32) Max() (k int32, d interface{}) { + return t.root.max().keyAndData() +} + +// Glb returns the greatest-lower-bound-exclusive of x and its associated +// data. If x has no glb in the tree, then (0, nil) is returned. +func (t *RBTint32) Glb(x int32) (k int32, d interface{}) { + return t.root.glb(x, false).keyAndData() +} + +// GlbEq returns the greatest-lower-bound-inclusive of x and its associated +// data. If x has no glbEQ in the tree, then (0, nil) is returned. +func (t *RBTint32) GlbEq(x int32) (k int32, d interface{}) { + return t.root.glb(x, true).keyAndData() +} + +// Lub returns the least-upper-bound-exclusive of x and its associated +// data. If x has no lub in the tree, then (0, nil) is returned. +func (t *RBTint32) Lub(x int32) (k int32, d interface{}) { + return t.root.lub(x, false).keyAndData() +} + +// LubEq returns the least-upper-bound-inclusive of x and its associated +// data. If x has no lubEq in the tree, then (0, nil) is returned. +func (t *RBTint32) LubEq(x int32) (k int32, d interface{}) { + return t.root.lub(x, true).keyAndData() +} + +func (t *node32) isLeaf() bool { + return t.left == nil && t.right == nil +} + +func (t *node32) visitInOrder(f func(int32, interface{})) { + if t.left != nil { + t.left.visitInOrder(f) + } + f(t.key, t.data) + if t.right != nil { + t.right.visitInOrder(f) + } +} + +func (t *node32) maxChildRank() rbrank { + if t.left == nil { + if t.right == nil { + return rankZero + } + return t.right.rank + } + if t.right == nil { + return t.left.rank + } + if t.right.rank > t.left.rank { + return t.right.rank + } + return t.left.rank +} + +func (t *node32) minChildRank() rbrank { + if t.left == nil || t.right == nil { + return rankZero + } + if t.right.rank < t.left.rank { + return t.right.rank + } + return t.left.rank +} + +func (t *node32) find(key int32) *node32 { + for t != nil { + if key < t.key { + t = t.left + } else if key > t.key { + t = t.right + } else { + return t + } + } + return nil +} + +func (t *node32) min() *node32 { + if t == nil { + return t + } + for t.left != nil { + t = t.left + } + return t +} + +func (t *node32) max() *node32 { + if t == nil { + return t + } + for t.right != nil { + t = t.right + } + return t +} + +func (t *node32) glb(key int32, allow_eq bool) *node32 { + var best *node32 = nil + for t != nil { + if key <= t.key { + if key == t.key && allow_eq { + return t + } + // t is too big, glb is to left. + t = t.left + } else { + // t is a lower bound, record it and seek a better one. + best = t + t = t.right + } + } + return best +} + +func (t *node32) lub(key int32, allow_eq bool) *node32 { + var best *node32 = nil + for t != nil { + if key >= t.key { + if key == t.key && allow_eq { + return t + } + // t is too small, lub is to right. + t = t.right + } else { + // t is a upper bound, record it and seek a better one. + best = t + t = t.left + } + } + return best +} + +func (t *node32) insert(x int32, w *RBTint32) (newroot, newnode *node32) { + // defaults + newroot = t + newnode = t + if x == t.key { + return + } + if x < t.key { + if t.left == nil { + n := w.makeNode(x) + n.parent = t + t.left = n + newnode = n + return + } + var new_l *node32 + new_l, newnode = t.left.insert(x, w) + t.left = new_l + new_l.parent = t + newrank := 1 + new_l.maxChildRank() + if newrank > t.rank { + if newrank > 1+t.right.Rank() { // rotations required + if new_l.left.Rank() < new_l.right.Rank() { + // double rotation + t.left = new_l.rightToRoot() + } + newroot = t.leftToRoot() + return + } else { + t.rank = newrank + } + } + } else { // x > t.key + if t.right == nil { + n := w.makeNode(x) + n.parent = t + t.right = n + newnode = n + return + } + var new_r *node32 + new_r, newnode = t.right.insert(x, w) + t.right = new_r + new_r.parent = t + newrank := 1 + new_r.maxChildRank() + if newrank > t.rank { + if newrank > 1+t.left.Rank() { // rotations required + if new_r.right.Rank() < new_r.left.Rank() { + // double rotation + t.right = new_r.leftToRoot() + } + newroot = t.rightToRoot() + return + } else { + t.rank = newrank + } + } + } + return +} + +func (t *node32) rightToRoot() *node32 { + // this + // left right + // rl rr + // + // becomes + // + // right + // this rr + // left rl + // + right := t.right + rl := right.left + right.parent = t.parent + right.left = t + t.parent = right + // parent's child ptr fixed in caller + t.right = rl + if rl != nil { + rl.parent = t + } + return right +} + +func (t *node32) leftToRoot() *node32 { + // this + // left right + // ll lr + // + // becomes + // + // left + // ll this + // lr right + // + left := t.left + lr := left.right + left.parent = t.parent + left.right = t + t.parent = left + // parent's child ptr fixed in caller + t.left = lr + if lr != nil { + lr.parent = t + } + return left +} + +// next returns the successor of t in a left-to-right +// walk of the tree in which t is embedded. +func (t *node32) next() *node32 { + // If there is a right child, it is to the right + r := t.right + if r != nil { + return r.min() + } + // if t is p.left, then p, else repeat. + p := t.parent + for p != nil { + if p.left == t { + return p + } + t = p + p = t.parent + } + return nil +} + +// prev returns the predecessor of t in a left-to-right +// walk of the tree in which t is embedded. +func (t *node32) prev() *node32 { + // If there is a left child, it is to the left + l := t.left + if l != nil { + return l.max() + } + // if t is p.right, then p, else repeat. + p := t.parent + for p != nil { + if p.right == t { + return p + } + t = p + p = t.parent + } + return nil +} diff --git a/src/cmd/compile/internal/ssa/redblack32_test.go b/src/cmd/compile/internal/ssa/redblack32_test.go new file mode 100644 index 00000000000000..6d72a3eee5f800 --- /dev/null +++ b/src/cmd/compile/internal/ssa/redblack32_test.go @@ -0,0 +1,276 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssa + +import ( + "fmt" + "testing" +) + +type sstring string + +func (s sstring) String() string { + return string(s) +} + +// wellFormed ensures that a red-black tree meets +// all of its invariants and returns a string identifying +// the first problem encountered. If there is no problem +// then the returned string is empty. The size is also +// returned to allow comparison of calculated tree size +// with expected. +func (t *RBTint32) wellFormed() (s string, i int) { + if t.root == nil { + s = "" + i = 0 + return + } + return t.root.wellFormedSubtree(nil, -0x80000000, 0x7fffffff) +} + +// wellFormedSubtree ensures that a red-black subtree meets +// all of its invariants and returns a string identifying +// the first problem encountered. If there is no problem +// then the returned string is empty. The size is also +// returned to allow comparison of calculated tree size +// with expected. +func (t *node32) wellFormedSubtree(parent *node32, min, max int32) (s string, i int) { + i = -1 // initialize to a failing value + s = "" // s is the reason for failure; empty means okay. + + if t.parent != parent { + s = "t.parent != parent" + return + } + + if min >= t.key { + s = "min >= t.key" + return + } + + if max <= t.key { + s = "max <= t.key" + return + } + + l := t.left + r := t.right + if l == nil && r == nil { + if t.rank != rankLeaf { + s = "leaf rank wrong" + return + } + } + if l != nil { + if t.rank < l.rank { + s = "t.rank < l.rank" + } else if t.rank > 1+l.rank { + s = "t.rank > 1+l.rank" + } else if t.rank <= l.maxChildRank() { + s = "t.rank <= l.maxChildRank()" + } else if t.key <= l.key { + s = "t.key <= l.key" + } + if s != "" { + return + } + } else { + if t.rank != 1 { + s = "t w/ left nil has rank != 1" + return + } + } + if r != nil { + if t.rank < r.rank { + s = "t.rank < r.rank" + } else if t.rank > 1+r.rank { + s = "t.rank > 1+r.rank" + } else if t.rank <= r.maxChildRank() { + s = "t.rank <= r.maxChildRank()" + } else if t.key >= r.key { + s = "t.key >= r.key" + } + if s != "" { + return + } + } else { + if t.rank != 1 { + s = "t w/ right nil has rank != 1" + return + } + } + ii := 1 + if l != nil { + res, il := l.wellFormedSubtree(t, min, t.key) + if res != "" { + s = "L." + res + return + } + ii += il + } + if r != nil { + res, ir := r.wellFormedSubtree(t, t.key, max) + if res != "" { + s = "R." + res + return + } + ii += ir + } + i = ii + return +} + +func (t *RBTint32) DebugString() string { + if t.root == nil { + return "" + } + return t.root.DebugString() +} + +// DebugString prints the tree with nested information +// to allow an eyeball check on the tree balance. +func (t *node32) DebugString() string { + s := "" + if t.left != nil { + s = s + "[" + s = s + t.left.DebugString() + s = s + "]" + } + s = s + fmt.Sprintf("%v=%v:%d", t.key, t.data, t.rank) + if t.right != nil { + s = s + "[" + s = s + t.right.DebugString() + s = s + "]" + } + return s +} + +func allRBT32Ops(te *testing.T, x []int32) { + t := &RBTint32{} + for i, d := range x { + x[i] = d + d // Double everything for glb/lub testing + } + + // fmt.Printf("Inserting double of %v", x) + k := 0 + min := int32(0x7fffffff) + max := int32(-0x80000000) + for _, d := range x { + if d < min { + min = d + } + + if d > max { + max = d + } + + t.Insert(d, sstring(fmt.Sprintf("%v", d))) + k++ + s, i := t.wellFormed() + if i != k { + te.Errorf("Wrong tree size %v, expected %v for %v", i, k, t.DebugString()) + } + if s != "" { + te.Errorf("Tree consistency problem at %v", s) + return + } else { + // fmt.Printf("%s", t.DebugString()) + } + } + + oops := false + + for _, d := range x { + s := fmt.Sprintf("%v", d) + f := t.Find(d) + + // data + if s != fmt.Sprintf("%v", f) { + te.Errorf("s(%v) != f(%v)", s, f) + oops = true + } + } + + if !oops { + for _, d := range x { + s := fmt.Sprintf("%v", d) + + kg, g := t.Glb(d + 1) + kge, ge := t.GlbEq(d) + kl, l := t.Lub(d - 1) + kle, le := t.LubEq(d) + + // keys + if d != kg { + te.Errorf("d(%v) != kg(%v)", d, kg) + } + if d != kl { + te.Errorf("d(%v) != kl(%v)", d, kl) + } + if d != kge { + te.Errorf("d(%v) != kge(%v)", d, kge) + } + if d != kle { + te.Errorf("d(%v) != kle(%v)", d, kle) + } + // data + if s != fmt.Sprintf("%v", g) { + te.Errorf("s(%v) != g(%v)", s, g) + } + if s != fmt.Sprintf("%v", l) { + te.Errorf("s(%v) != l(%v)", s, l) + } + if s != fmt.Sprintf("%v", ge) { + te.Errorf("s(%v) != ge(%v)", s, ge) + } + if s != fmt.Sprintf("%v", le) { + te.Errorf("s(%v) != le(%v)", s, le) + } + } + + for _, d := range x { + s := fmt.Sprintf("%v", d) + kge, ge := t.GlbEq(d + 1) + kle, le := t.LubEq(d - 1) + if d != kge { + te.Errorf("d(%v) != kge(%v)", d, kge) + } + if d != kle { + te.Errorf("d(%v) != kle(%v)", d, kle) + } + if s != fmt.Sprintf("%v", ge) { + te.Errorf("s(%v) != ge(%v)", s, ge) + } + if s != fmt.Sprintf("%v", le) { + te.Errorf("s(%v) != le(%v)", s, le) + } + } + + kg, g := t.Glb(min) + kge, ge := t.GlbEq(min - 1) + kl, l := t.Lub(max) + kle, le := t.LubEq(max + 1) + fmin := t.Find(min - 1) + fmax := t.Find(min + 11) + + if kg != 0 || kge != 0 || kl != 0 || kle != 0 { + te.Errorf("Got non-zero-key for missing query") + } + + if g != nil || ge != nil || l != nil || le != nil || fmin != nil || fmax != nil { + te.Errorf("Got non-error-data for missing query") + } + + } +} + +func TestAllRBTreeOps(t *testing.T) { + allRBT32Ops(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}) + allRBT32Ops(t, []int32{22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 3, 2, 1, 25, 24, 23, 12, 11, 10, 9, 8, 7, 6, 5, 4}) + allRBT32Ops(t, []int32{25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}) + allRBT32Ops(t, []int32{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}) + allRBT32Ops(t, []int32{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2}) + allRBT32Ops(t, []int32{24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25}) +} diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index e0dc1009afff1b..8603615f251720 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -106,7 +106,6 @@ package ssa import ( - "cmd/internal/obj" "fmt" "unsafe" ) @@ -456,7 +455,7 @@ func (s *regAllocState) init(f *Func) { s.allocatable = regMask(1)<= 0; i-- { v := ss.Values[i] - entryCandidates.remove(v.ID) // Cannot be an issue, only keeps the sets smaller. + vorig := s.orig[v.ID] + if vorig != nil { + entryCandidates.remove(vorig.ID) // Cannot be an issue, only keeps the sets smaller. + } for _, a := range v.Args { - if s.isLoopSpillCandidate(loop, a) { - entryCandidates.setBit(a.ID, uint(whichExit)) + aorig := s.orig[a.ID] + if aorig != nil && s.isLoopSpillCandidate(loop, aorig) { + entryCandidates.setBit(aorig.ID, uint(whichExit)) } } } @@ -1524,7 +1546,7 @@ sinking: } if f.pass.stats > 0 { - f.logStat("spills_info", + f.LogStat("spills_info", nSpills, "spills", nSpillsInner, "inner_spills_remaining", nSpillsSunk, "inner_spills_sunk", nSpillsSunkUnused, "inner_spills_unused", nSpillsNotSunkLateUse, "inner_spills_shuffled", nSpillsChanged, "inner_spills_changed") } } diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 7d6d2179f76425..03c38827cc433e 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -7,6 +7,8 @@ package ssa import ( "fmt" "math" + "os" + "path/filepath" ) func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) { @@ -357,3 +359,28 @@ func clobber(v *Value) bool { // Note: leave v.Block intact. The Block field is used after clobber. return true } + +// logRule logs the use of the rule s. This will only be enabled if +// rewrite rules were generated with the -log option, see gen/rulegen.go. +func logRule(s string) { + if ruleFile == nil { + // Open a log file to write log to. We open in append + // mode because all.bash runs the compiler lots of times, + // and we want the concatenation of all of those logs. + // This means, of course, that users need to rm the old log + // to get fresh data. + // TODO: all.bash runs compilers in parallel. Need to synchronize logging somehow? + w, err := os.OpenFile(filepath.Join(os.Getenv("GOROOT"), "src", "rulelog"), + os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + panic(err) + } + ruleFile = w + } + _, err := fmt.Fprintf(ruleFile, "rewrite %s\n", s) + if err != nil { + panic(err) + } +} + +var ruleFile *os.File diff --git a/src/cmd/compile/internal/ssa/sparsemap.go b/src/cmd/compile/internal/ssa/sparsemap.go index 0211a70f09d211..afb9f6049186cc 100644 --- a/src/cmd/compile/internal/ssa/sparsemap.go +++ b/src/cmd/compile/internal/ssa/sparsemap.go @@ -14,13 +14,13 @@ type sparseEntry struct { type sparseMap struct { dense []sparseEntry - sparse []int + sparse []int32 } // newSparseMap returns a sparseMap that can map // integers between 0 and n-1 to int32s. func newSparseMap(n int) *sparseMap { - return &sparseMap{nil, make([]int, n)} + return &sparseMap{dense: nil, sparse: make([]int32, n)} } func (s *sparseMap) size() int { @@ -29,14 +29,14 @@ func (s *sparseMap) size() int { func (s *sparseMap) contains(k ID) bool { i := s.sparse[k] - return i < len(s.dense) && s.dense[i].key == k + return i < int32(len(s.dense)) && s.dense[i].key == k } // get returns the value for key k, or -1 if k does // not appear in the map. func (s *sparseMap) get(k ID) int32 { i := s.sparse[k] - if i < len(s.dense) && s.dense[i].key == k { + if i < int32(len(s.dense)) && s.dense[i].key == k { return s.dense[i].val } return -1 @@ -44,12 +44,12 @@ func (s *sparseMap) get(k ID) int32 { func (s *sparseMap) set(k ID, v int32) { i := s.sparse[k] - if i < len(s.dense) && s.dense[i].key == k { + if i < int32(len(s.dense)) && s.dense[i].key == k { s.dense[i].val = v return } s.dense = append(s.dense, sparseEntry{k, v}) - s.sparse[k] = len(s.dense) - 1 + s.sparse[k] = int32(len(s.dense)) - 1 } // setBit sets the v'th bit of k's value, where 0 <= v < 32 @@ -58,17 +58,17 @@ func (s *sparseMap) setBit(k ID, v uint) { panic("bit index too large.") } i := s.sparse[k] - if i < len(s.dense) && s.dense[i].key == k { + if i < int32(len(s.dense)) && s.dense[i].key == k { s.dense[i].val |= 1 << v return } s.dense = append(s.dense, sparseEntry{k, 1 << v}) - s.sparse[k] = len(s.dense) - 1 + s.sparse[k] = int32(len(s.dense)) - 1 } func (s *sparseMap) remove(k ID) { i := s.sparse[k] - if i < len(s.dense) && s.dense[i].key == k { + if i < int32(len(s.dense)) && s.dense[i].key == k { y := s.dense[len(s.dense)-1] s.dense[i] = y s.sparse[y.key] = i diff --git a/src/cmd/compile/internal/ssa/sparseset.go b/src/cmd/compile/internal/ssa/sparseset.go index 66bebf139e66bf..b5cabfb0cdf083 100644 --- a/src/cmd/compile/internal/ssa/sparseset.go +++ b/src/cmd/compile/internal/ssa/sparseset.go @@ -9,13 +9,13 @@ package ssa type sparseSet struct { dense []ID - sparse []int + sparse []int32 } // newSparseSet returns a sparseSet that can represent // integers between 0 and n-1 func newSparseSet(n int) *sparseSet { - return &sparseSet{nil, make([]int, n)} + return &sparseSet{dense: nil, sparse: make([]int32, n)} } func (s *sparseSet) cap() int { @@ -28,16 +28,16 @@ func (s *sparseSet) size() int { func (s *sparseSet) contains(x ID) bool { i := s.sparse[x] - return i < len(s.dense) && s.dense[i] == x + return i < int32(len(s.dense)) && s.dense[i] == x } func (s *sparseSet) add(x ID) { i := s.sparse[x] - if i < len(s.dense) && s.dense[i] == x { + if i < int32(len(s.dense)) && s.dense[i] == x { return } s.dense = append(s.dense, x) - s.sparse[x] = len(s.dense) - 1 + s.sparse[x] = int32(len(s.dense)) - 1 } func (s *sparseSet) addAll(a []ID) { @@ -54,7 +54,7 @@ func (s *sparseSet) addAllValues(a []*Value) { func (s *sparseSet) remove(x ID) { i := s.sparse[x] - if i < len(s.dense) && s.dense[i] == x { + if i < int32(len(s.dense)) && s.dense[i] == x { y := s.dense[len(s.dense)-1] s.dense[i] = y s.sparse[y] = i diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go index 45c78974963cdf..7c82a60d0fa8fc 100644 --- a/src/cmd/compile/internal/ssa/sparsetree.go +++ b/src/cmd/compile/internal/ssa/sparsetree.go @@ -4,7 +4,9 @@ package ssa -type sparseTreeNode struct { +import "fmt" + +type SparseTreeNode struct { child *Block sibling *Block parent *Block @@ -20,26 +22,39 @@ type sparseTreeNode struct { entry, exit int32 } +func (s *SparseTreeNode) String() string { + return fmt.Sprintf("[%d,%d]", s.entry, s.exit) +} + +func (s *SparseTreeNode) Entry() int32 { + return s.entry +} + +func (s *SparseTreeNode) Exit() int32 { + return s.exit +} + const ( // When used to lookup up definitions in a sparse tree, // these adjustments to a block's entry (+adjust) and // exit (-adjust) numbers allow a distinction to be made // between assignments (typically branch-dependent - // conditionals) occurring "before" phi functions, the - // phi functions, and at the bottom of a block. - ADJUST_BEFORE = -1 // defined before phi - ADJUST_TOP = 0 // defined by phi - ADJUST_BOTTOM = 1 // defined within block + // conditionals) occurring "before" the block (e.g., as inputs + // to the block and its phi functions), "within" the block, + // and "after" the block. + AdjustBefore = -1 // defined before phi + AdjustWithin = 0 // defined by phi + AdjustAfter = 1 // defined within block ) -// A sparseTree is a tree of Blocks. +// A SparseTree is a tree of Blocks. // It allows rapid ancestor queries, // such as whether one block dominates another. -type sparseTree []sparseTreeNode +type SparseTree []SparseTreeNode -// newSparseTree creates a sparseTree from a block-to-parent map (array indexed by Block.ID) -func newSparseTree(f *Func, parentOf []*Block) sparseTree { - t := make(sparseTree, f.NumBlocks()) +// newSparseTree creates a SparseTree from a block-to-parent map (array indexed by Block.ID) +func newSparseTree(f *Func, parentOf []*Block) SparseTree { + t := make(SparseTree, f.NumBlocks()) for _, b := range f.Blocks { n := &t[b.ID] if p := parentOf[b.ID]; p != nil { @@ -80,7 +95,7 @@ func newSparseTree(f *Func, parentOf []*Block) sparseTree { // root left left right right root // 1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18 -func (t sparseTree) numberBlock(b *Block, n int32) int32 { +func (t SparseTree) numberBlock(b *Block, n int32) int32 { // reserve n for entry-1, assign n+1 to entry n++ t[b.ID].entry = n @@ -103,19 +118,19 @@ func (t sparseTree) numberBlock(b *Block, n int32) int32 { // to assign entry and exit numbers in the treewalk, those // numbers are also consistent with this order (i.e., // Sibling(x) has entry number larger than x's exit number). -func (t sparseTree) Sibling(x *Block) *Block { +func (t SparseTree) Sibling(x *Block) *Block { return t[x.ID].sibling } // Child returns a child of x in the dominator tree, or // nil if there are none. The choice of first child is // arbitrary but repeatable. -func (t sparseTree) Child(x *Block) *Block { +func (t SparseTree) Child(x *Block) *Block { return t[x.ID].child } // isAncestorEq reports whether x is an ancestor of or equal to y. -func (t sparseTree) isAncestorEq(x, y *Block) bool { +func (t SparseTree) isAncestorEq(x, y *Block) bool { if x == y { return true } @@ -125,7 +140,7 @@ func (t sparseTree) isAncestorEq(x, y *Block) bool { } // isAncestor reports whether x is a strict ancestor of y. -func (t sparseTree) isAncestor(x, y *Block) bool { +func (t SparseTree) isAncestor(x, y *Block) bool { if x == y { return false } @@ -134,8 +149,38 @@ func (t sparseTree) isAncestor(x, y *Block) bool { return xx.entry < yy.entry && yy.exit < xx.exit } -// maxdomorder returns a value to allow a maximal dominator first sort. maxdomorder(x) < maxdomorder(y) is true -// if x may dominate y, and false if x cannot dominate y. -func (t sparseTree) maxdomorder(x *Block) int32 { +// domorder returns a value for dominator-oriented sorting. +// Block domination does not provide a total ordering, +// but domorder two has useful properties. +// (1) If domorder(x) > domorder(y) then x does not dominate y. +// (2) If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y, +// then x does not dominate z. +// Property (1) means that blocks sorted by domorder always have a maximal dominant block first. +// Property (2) allows searches for dominated blocks to exit early. +func (t SparseTree) domorder(x *Block) int32 { + // Here is an argument that entry(x) provides the properties documented above. + // + // Entry and exit values are assigned in a depth-first dominator tree walk. + // For all blocks x and y, one of the following holds: + // + // (x-dom-y) x dominates y => entry(x) < entry(y) < exit(y) < exit(x) + // (y-dom-x) y dominates x => entry(y) < entry(x) < exit(x) < exit(y) + // (x-then-y) neither x nor y dominates the other and x walked before y => entry(x) < exit(x) < entry(y) < exit(y) + // (y-then-x) neither x nor y dominates the other and y walked before y => entry(y) < exit(y) < entry(x) < exit(x) + // + // entry(x) > entry(y) eliminates case x-dom-y. This provides property (1) above. + // + // For property (2), assume entry(x) < entry(y) and entry(y) < entry(z) and x does not dominate y. + // entry(x) < entry(y) allows cases x-dom-y and x-then-y. + // But by supposition, x does not dominate y. So we have x-then-y. + // + // For contractidion, assume x dominates z. + // Then entry(x) < entry(z) < exit(z) < exit(x). + // But we know x-then-y, so entry(x) < exit(x) < entry(y) < exit(y). + // Combining those, entry(x) < entry(z) < exit(z) < exit(x) < entry(y) < exit(y). + // By supposition, entry(y) < entry(z), which allows cases y-dom-z and y-then-z. + // y-dom-z requires entry(y) < entry(z), but we have entry(z) < entry(y). + // y-then-z requires exit(y) < entry(z), but we have entry(z) < exit(y). + // We have a contradiction, so x does not dominate z, as required. return t[x.ID].entry } diff --git a/src/cmd/compile/internal/ssa/sparsetreemap.go b/src/cmd/compile/internal/ssa/sparsetreemap.go new file mode 100644 index 00000000000000..61276985b114bb --- /dev/null +++ b/src/cmd/compile/internal/ssa/sparsetreemap.go @@ -0,0 +1,169 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssa + +import "fmt" + +// A SparseTreeMap encodes a subset of nodes within a tree +// used for sparse-ancestor queries. +// +// Combined with a SparseTreeHelper, this supports an Insert +// to add a tree node to the set and a Find operation to locate +// the nearest tree ancestor of a given node such that the +// ancestor is also in the set. +// +// Given a set of blocks {B1, B2, B3} within the dominator tree, established by +// stm.Insert()ing B1, B2, B3, etc, a query at block B +// (performed with stm.Find(stm, B, adjust, helper)) +// will return the member of the set that is the nearest strict +// ancestor of B within the dominator tree, or nil if none exists. +// The expected complexity of this operation is the log of the size +// the set, given certain assumptions about sparsity (the log complexity +// could be guaranteed with additional data structures whose constant- +// factor overhead has not yet been justified.) +// +// The adjust parameter allows positioning of the insertion +// and lookup points within a block -- one of +// AdjustBefore, AdjustWithin, AdjustAfter, +// where lookups at AdjustWithin can find insertions at +// AdjustBefore in the same block, and lookups at AdjustAfter +// can find insertions at either AdjustBefore or AdjustWithin +// in the same block. (Note that this assumes a gappy numbering +// such that exit number or exit number is separated from its +// nearest neighbor by at least 3). +// +// The Sparse Tree lookup algorithm is described by +// Paul F. Dietz. Maintaining order in a linked list. In +// Proceedings of the Fourteenth Annual ACM Symposium on +// Theory of Computing, pages 122–127, May 1982. +// and by +// Ben Wegbreit. Faster retrieval from context trees. +// Communications of the ACM, 19(9):526–529, September 1976. +type SparseTreeMap RBTint32 + +// A SparseTreeHelper contains indexing and allocation data +// structures common to a collection of SparseTreeMaps, as well +// as exposing some useful control-flow-related data to other +// packages, such as gc. +type SparseTreeHelper struct { + Sdom []SparseTreeNode // indexed by block.ID + Po []*Block // exported data + Dom []*Block // exported data + Ponums []int32 // exported data +} + +// NewSparseTreeHelper returns a SparseTreeHelper for use +// in the gc package, for example in phi-function placement. +func NewSparseTreeHelper(f *Func) *SparseTreeHelper { + dom := dominators(f) + ponums := make([]int32, f.NumBlocks()) + po := postorderWithNumbering(f, ponums) + return makeSparseTreeHelper(newSparseTree(f, dom), dom, po, ponums) +} + +func (h *SparseTreeHelper) NewTree() *SparseTreeMap { + return &SparseTreeMap{} +} + +func makeSparseTreeHelper(sdom SparseTree, dom, po []*Block, ponums []int32) *SparseTreeHelper { + helper := &SparseTreeHelper{Sdom: []SparseTreeNode(sdom), + Dom: dom, + Po: po, + Ponums: ponums, + } + return helper +} + +// A sparseTreeMapEntry contains the data stored in a binary search +// data structure indexed by (dominator tree walk) entry and exit numbers. +// Each entry is added twice, once keyed by entry-1/entry/entry+1 and +// once keyed by exit+1/exit/exit-1. (there are three choices of paired indices, not 9, and they properly nest) +type sparseTreeMapEntry struct { + index *SparseTreeNode + block *Block // TODO: store this in a separate index. + data interface{} +} + +// Insert creates a definition within b with data x. +// adjust indicates where in the block should be inserted: +// AdjustBefore means defined at a phi function (visible Within or After in the same block) +// AdjustWithin means defined within the block (visible After in the same block) +// AdjustAfter means after the block (visible within child blocks) +func (m *SparseTreeMap) Insert(b *Block, adjust int32, x interface{}, helper *SparseTreeHelper) { + rbtree := (*RBTint32)(m) + blockIndex := &helper.Sdom[b.ID] + if blockIndex.entry == 0 { + // assert unreachable + return + } + entry := &sparseTreeMapEntry{index: blockIndex, data: x} + right := blockIndex.exit - adjust + _ = rbtree.Insert(right, entry) + + left := blockIndex.entry + adjust + _ = rbtree.Insert(left, entry) +} + +// Find returns the definition visible from block b, or nil if none can be found. +// Adjust indicates where the block should be searched. +// AdjustBefore searches before the phi functions of b. +// AdjustWithin searches starting at the phi functions of b. +// AdjustAfter searches starting at the exit from the block, including normal within-block definitions. +// +// Note that Finds are properly nested with Inserts: +// m.Insert(b, a) followed by m.Find(b, a) will not return the result of the insert, +// but m.Insert(b, AdjustBefore) followed by m.Find(b, AdjustWithin) will. +// +// Another way to think of this is that Find searches for inputs, Insert defines outputs. +func (m *SparseTreeMap) Find(b *Block, adjust int32, helper *SparseTreeHelper) interface{} { + rbtree := (*RBTint32)(m) + if rbtree == nil { + return nil + } + blockIndex := &helper.Sdom[b.ID] + _, v := rbtree.Glb(blockIndex.entry + adjust) + for v != nil { + otherEntry := v.(*sparseTreeMapEntry) + otherIndex := otherEntry.index + // Two cases -- either otherIndex brackets blockIndex, + // or it doesn't. + // + // Note that if otherIndex and blockIndex are + // the same block, then the glb test only passed + // because the definition is "before", + // i.e., k == blockIndex.entry-1 + // allowing equality is okay on the blocks check. + if otherIndex.exit >= blockIndex.exit { + // bracketed. + return otherEntry.data + } + // In the not-bracketed case, we could memoize the results of + // walking up the tree, but for now we won't. + // Memoize plan is to take the gap (inclusive) + // from otherIndex.exit+1 to blockIndex.entry-1 + // and insert it into this or a second tree. + // Said tree would then need adjusting whenever + // an insertion occurred. + + // Expectation is that per-variable tree is sparse, + // therefore probe siblings instead of climbing up. + // Note that each sibling encountered in this walk + // to find a defining ancestor shares that ancestor + // because the walk skips over the interior -- each + // Glb will be an exit, and the iteration is to the + // Glb of the entry. + _, v = rbtree.Glb(otherIndex.entry - 1) + } + return nil // nothing found +} + +func (m *SparseTreeMap) String() string { + tree := (*RBTint32)(m) + return tree.String() +} + +func (e *sparseTreeMapEntry) String() string { + return fmt.Sprintf("index=%v, data=%v", e.index, e.data) +} diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index c08ed79cd65fe9..83f65d093b7c5c 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -84,7 +84,7 @@ func stackalloc(f *Func, spillLive [][]ID) [][]ID { s.stackalloc() if f.pass.stats > 0 { - f.logStat("stack_alloc_stats", + f.LogStat("stack_alloc_stats", s.nArgSlot, "arg_slots", s.nNotNeed, "slot_not_needed", s.nNamedSlot, "named_slots", s.nAuto, "auto_slots", s.nReuse, "reused_slots", s.nSelfInterfere, "self_interfering") diff --git a/src/cmd/compile/internal/x86/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go index f432cd8933f93d..6406326b60e050 100644 --- a/src/cmd/compile/internal/x86/gsubr.go +++ b/src/cmd/compile/internal/x86/gsubr.go @@ -643,7 +643,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { base = n1.Left } - if base.Op == gc.ONAME && base.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG { + if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG { r1 = *n1 } else { gc.Regalloc(&r1, t, n1) @@ -724,17 +724,8 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { n = &n1 - case gc.ONAME: - if n.Class == gc.PPARAMREF { - var n1 gc.Node - gc.Cgen(n.Name.Heapaddr, &n1) - sclean[nsclean-1] = n1 - n = &n1 - } - + case gc.ONAME, gc.OINDREG: // nothing - case gc.OINDREG: - break } *lo = *n diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go index 46495950e98f53..a9ed66eea06cd5 100644 --- a/src/cmd/cover/cover.go +++ b/src/cmd/cover/cover.go @@ -388,7 +388,7 @@ func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup { } } if list != nil { - comments = append(comments, &ast.CommentGroup{list}) + comments = append(comments, &ast.CommentGroup{List: list}) } } return comments diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 04a13b2365ce6a..aa12aa9dc3da8c 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -464,6 +464,7 @@ var deptab = []struct { }{ {"cmd/go", []string{ "zdefaultcc.go", + "zosarch.go", }}, {"runtime/internal/sys", []string{ "zversion.go", @@ -485,6 +486,7 @@ var gentab = []struct { gen func(string, string) }{ {"zdefaultcc.go", mkzdefaultcc}, + {"zosarch.go", mkzosarch}, {"zversion.go", mkzversion}, {"zcgo.go", mkzcgo}, diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go index 2b68fc222460c8..c367c70b043089 100644 --- a/src/cmd/dist/buildgo.go +++ b/src/cmd/dist/buildgo.go @@ -23,9 +23,7 @@ import ( // It is invoked to write cmd/go/zdefaultcc.go // but we also write cmd/cgo/zdefaultcc.go func mkzdefaultcc(dir, file string) { - var out string - - out = fmt.Sprintf( + out := fmt.Sprintf( "// auto generated by go tool dist\n"+ "\n"+ "package main\n"+ @@ -42,7 +40,16 @@ func mkzdefaultcc(dir, file string) { writefile(out, file, writeSkipSame) } -// mkzcgo writes zcgo.go for go/build package: +// mkzcgo writes zosarch.go for cmd/go. +func mkzosarch(dir, file string) { + var buf bytes.Buffer + buf.WriteString("// auto generated by go tool dist\n\n") + buf.WriteString("package main\n\n") + fmt.Fprintf(&buf, "var osArchSupportsCgo = %#v", cgoEnabled) + writefile(buf.String(), file, writeSkipSame) +} + +// mkzcgo writes zcgo.go for the go/build package: // // package build // var cgoEnabled = map[string]bool{} diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 3d123c2c8641da..1a1f7d961bea77 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -699,7 +699,7 @@ func (t *tester) supportedBuildmode(mode string) bool { } return false default: - log.Fatal("internal error: unknown buildmode %s", mode) + log.Fatalf("internal error: unknown buildmode %s", mode) return false } } diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 2b74cb59e35c58..2a64657732067c 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -570,6 +570,8 @@ syntax of package template. The default output is equivalent to -f Stale bool // would 'go install' do anything for this package? StaleReason string // explanation for Stale==true Root string // Go root or Go path dir containing this package + ConflictDir string // this directory shadows Dir in $GOPATH + BinaryOnly bool // binary-only package: cannot be recompiled from sources // Source files GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) @@ -704,6 +706,9 @@ Each listed package causes the execution of a separate test binary. Test files that declare a package with the suffix "_test" will be compiled as a separate package, and then linked and run with the main test binary. +The go tool will ignore a directory named "testdata", making it available +to hold ancillary data needed by the tests. + By default, go test needs no arguments. It compiles and tests the package with source in the current directory, including tests, and runs the tests. @@ -882,7 +887,15 @@ the extension of the file name. These extensions are: Files of each of these types except .syso may contain build constraints, but the go command stops scanning for build constraints at the first item in the file that is not a blank line or //-style -line comment. +line comment. See the go/build package documentation for +more details. + +Non-test Go source files can also include a //go:binary-only-package +comment, indicating that the package sources are included +for documentation only and must not be used to build the +package binary. This enables distribution of Go packages in +their compiled form alone. See the go/build package documentation +for more details. GOPATH environment variable @@ -1457,7 +1470,6 @@ control the execution of any test: -trace trace.out Write an execution trace to the specified file before exiting. - Writes test binary as -c would. -v Verbose output: log all tests as they are run. Also print all diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 0102b5e08a2272..e0cb216b8c4e56 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -335,7 +335,7 @@ func buildModeInit() { return p } switch platform { - case "darwin/arm": + case "darwin/arm", "darwin/arm64": codegenArg = "-shared" default: } @@ -361,6 +361,9 @@ func buildModeInit() { case "android/arm", "android/arm64", "android/amd64", "android/386": codegenArg = "-shared" ldBuildmode = "pie" + case "darwin/arm", "darwin/arm64": + codegenArg = "-shared" + fallthrough default: ldBuildmode = "exe" } @@ -669,6 +672,12 @@ var ( func init() { goarch = buildContext.GOARCH goos = buildContext.GOOS + + if _, ok := osArchSupportsCgo[goos+"/"+goarch]; !ok { + fmt.Fprintf(os.Stderr, "cmd/go: unsupported GOOS/GOARCH pair %s/%s\n", goos, goarch) + os.Exit(2) + } + if goos == "windows" { exeSuffix = ".exe" } @@ -3097,6 +3106,8 @@ func (b *builder) gccArchArgs() []string { return []string{"-marm"} // not thumb case "s390x": return []string{"-m64", "-march=z196"} + case "mips64", "mips64le": + return []string{"-mabi=64"} } return nil } diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index ac82b2ffeb467b..50e6b500dab3d7 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -580,32 +580,6 @@ func (tg *testgoData) cleanup() { } } -// resetReadOnlyFlagAll resets windows read-only flag -// set on path and any children it contains. -// The flag is set by git and has to be removed. -// os.Remove refuses to remove files with read-only flag set. -func (tg *testgoData) resetReadOnlyFlagAll(path string) { - fi, err := os.Stat(path) - if err != nil { - tg.t.Fatalf("resetReadOnlyFlagAll(%q) failed: %v", path, err) - } - if !fi.IsDir() { - err := os.Chmod(path, 0666) - if err != nil { - tg.t.Fatalf("resetReadOnlyFlagAll(%q) failed: %v", path, err) - } - } - fd, err := os.Open(path) - if err != nil { - tg.t.Fatalf("resetReadOnlyFlagAll(%q) failed: %v", path, err) - } - defer fd.Close() - names, _ := fd.Readdirnames(-1) - for _, name := range names { - tg.resetReadOnlyFlagAll(path + string(filepath.Separator) + name) - } -} - // failSSH puts an ssh executable in the PATH that always fails. // This is to stub out uses of ssh by go get. func (tg *testgoData) failSSH() { @@ -1177,7 +1151,7 @@ func TestImportCommentConflict(t *testing.T) { tg.grepStderr("found import comments", "go build did not mention comment conflict") } -// cmd/go: custom import path checking should not apply to github.com/xxx/yyy. +// cmd/go: custom import path checking should not apply to Go packages without import comment. func TestIssue10952(t *testing.T) { testenv.MustHaveExternalNetwork(t) if _, err := exec.LookPath("git"); err != nil { @@ -1192,11 +1166,38 @@ func TestIssue10952(t *testing.T) { const importPath = "github.com/zombiezen/go-get-issue-10952" tg.run("get", "-d", "-u", importPath) repoDir := tg.path("src/" + importPath) - defer tg.resetReadOnlyFlagAll(repoDir) tg.runGit(repoDir, "remote", "set-url", "origin", "https://"+importPath+".git") tg.run("get", "-d", "-u", importPath) } +// Test git clone URL that uses SCP-like syntax and custom import path checking. +func TestIssue11457(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + if _, err := exec.LookPath("git"); err != nil { + t.Skip("skipping because git binary not found") + } + + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempDir("src") + tg.setenv("GOPATH", tg.path(".")) + const importPath = "github.com/rsc/go-get-issue-11457" + tg.run("get", "-d", "-u", importPath) + repoDir := tg.path("src/" + importPath) + tg.runGit(repoDir, "remote", "set-url", "origin", "git@github.com:rsc/go-get-issue-11457") + + // At this time, custom import path checking compares remotes verbatim (rather than + // just the host and path, skipping scheme and user), so we expect go get -u to fail. + // However, the goal of this test is to verify that gitRemoteRepo correctly parsed + // the SCP-like syntax, and we expect it to appear in the error message. + tg.runFail("get", "-d", "-u", importPath) + want := " is checked out from ssh://git@github.com/rsc/go-get-issue-11457" + if !strings.HasSuffix(strings.TrimSpace(tg.getStderr()), want) { + t.Error("expected clone URL to appear in stderr") + } +} + func TestGetGitDefaultBranch(t *testing.T) { testenv.MustHaveExternalNetwork(t) if _, err := exec.LookPath("git"); err != nil { @@ -1216,7 +1217,6 @@ func TestGetGitDefaultBranch(t *testing.T) { tg.run("get", "-d", importPath) repoDir := tg.path("src/" + importPath) - defer tg.resetReadOnlyFlagAll(repoDir) tg.runGit(repoDir, "branch", "--contains", "HEAD") tg.grepStdout(`\* another-branch`, "not on correct default branch") @@ -2832,7 +2832,8 @@ func TestBinaryOnlyPackages(t *testing.T) { os.Remove(tg.path("src/p1/p1.go")) tg.mustNotExist(tg.path("src/p1/p1.go")) - tg.tempFile("src/p2/p2.go", ` + tg.tempFile("src/p2/p2.go", `//go:binary-only-packages-are-not-great + package p2 import "p1" func F() { p1.F(true) } @@ -2841,7 +2842,7 @@ func TestBinaryOnlyPackages(t *testing.T) { tg.grepStderr("no buildable Go source files", "did not complain about missing sources") tg.tempFile("src/p1/missing.go", `//go:binary-only-package - + package p1 func G() `) diff --git a/src/cmd/go/note.go b/src/cmd/go/note.go index ada8ddded47f28..fae9536d13fb93 100644 --- a/src/cmd/go/note.go +++ b/src/cmd/go/note.go @@ -110,7 +110,7 @@ func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, // or even the first few megabytes of the file // due to differences in note segment placement; // in that case, extract the note data manually. - _, err = f.Seek(int64(p.Off), 0) + _, err = f.Seek(int64(p.Off), io.SeekStart) if err != nil { return "", err } diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 02abcbe23a07f0..bc5982e61ce9cf 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -59,6 +59,9 @@ Each listed package causes the execution of a separate test binary. Test files that declare a package with the suffix "_test" will be compiled as a separate package, and then linked and run with the main test binary. +The go tool will ignore a directory named "testdata", making it available +to hold ancillary data needed by the tests. + By default, go test needs no arguments. It compiles and tests the package with source in the current directory, including tests, and runs the tests. diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 4ff71f21683125..10b8cf8c49dcfa 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -171,10 +171,10 @@ func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error // Eg, "git@github.com:user/repo" becomes // "ssh://git@github.com/user/repo". repoURL = &url.URL{ - Scheme: "ssh", - User: url.User(m[1]), - Host: m[2], - RawPath: m[3], + Scheme: "ssh", + User: url.User(m[1]), + Host: m[2], + Path: m[3], } } else { repoURL, err = url.Parse(out) @@ -848,6 +848,15 @@ var vcsPaths = []*vcsPath{ repo: "https://{root}", }, + // Git at OpenStack + { + prefix: "git.openstack.org", + re: `^(?Pgit\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(\.git)?(/[A-Za-z0-9_.\-]+)*$`, + vcs: "git", + repo: "https://{root}", + check: noVCSSuffix, + }, + // General syntax for any server. // Must be last. { diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go index d9511894598704..06650608ba40a8 100644 --- a/src/cmd/go/vcs_test.go +++ b/src/cmd/go/vcs_test.go @@ -86,6 +86,39 @@ func TestRepoRootForImportPath(t *testing.T) { "hub.jazz.net/git/USER/pkgname", nil, }, + // OpenStack tests + { + "git.openstack.org/openstack/swift", + &repoRoot{ + vcs: vcsGit, + repo: "https://git.openstack.org/openstack/swift", + }, + }, + // Trailing .git is less preferred but included for + // compatibility purposes while the same source needs to + // be compilable on both old and new go + { + "git.openstack.org/openstack/swift.git", + &repoRoot{ + vcs: vcsGit, + repo: "https://git.openstack.org/openstack/swift", + }, + }, + { + "git.openstack.org/openstack/swift/go/hummingbird", + &repoRoot{ + vcs: vcsGit, + repo: "https://git.openstack.org/openstack/swift", + }, + }, + { + "git.openstack.org", + nil, + }, + { + "git.openstack.org/openstack", + nil, + }, // Spaces are not valid in package name { "git.apache.org/package name/path/to/lib", diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go index 69fa496110db43..214f65cbc4cbdf 100644 --- a/src/cmd/internal/goobj/read.go +++ b/src/cmd/internal/goobj/read.go @@ -290,9 +290,9 @@ func importPathToPrefix(s string) string { func (r *objReader) init(f io.ReadSeeker, p *Package) { r.f = f r.p = p - r.offset, _ = f.Seek(0, 1) - r.limit, _ = f.Seek(0, 2) - f.Seek(r.offset, 0) + r.offset, _ = f.Seek(0, io.SeekCurrent) + r.limit, _ = f.Seek(0, io.SeekEnd) + f.Seek(r.offset, io.SeekStart) r.b = bufio.NewReader(f) r.pkgprefix = importPathToPrefix(p.ImportPath) + "." } @@ -440,7 +440,7 @@ func (r *objReader) skip(n int64) { r.readFull(r.tmp[:n]) } else { // Seek, giving up buffered data. - _, err := r.f.Seek(r.offset+n, 0) + _, err := r.f.Seek(r.offset+n, io.SeekStart) if err != nil { r.error(err) } diff --git a/src/cmd/internal/obj/go.go b/src/cmd/internal/obj/go.go index 484bb472d09e16..1852dc74f63b7d 100644 --- a/src/cmd/internal/obj/go.go +++ b/src/cmd/internal/obj/go.go @@ -13,7 +13,7 @@ import ( // go-specific code shared across loaders (5l, 6l, 8l). var ( - Framepointer_enabled int + framepointer_enabled int Fieldtrack_enabled int ) @@ -26,14 +26,21 @@ var exper = []struct { val *int }{ {"fieldtrack", &Fieldtrack_enabled}, - {"framepointer", &Framepointer_enabled}, + {"framepointer", &framepointer_enabled}, } func addexp(s string) { + // Could do general integer parsing here, but the runtime copy doesn't yet. + v := 1 + name := s + if len(name) > 2 && name[:2] == "no" { + v = 0 + name = name[2:] + } for i := 0; i < len(exper); i++ { - if exper[i].name == s { + if exper[i].name == name { if exper[i].val != nil { - *exper[i].val = 1 + *exper[i].val = v } return } @@ -44,6 +51,7 @@ func addexp(s string) { } func init() { + framepointer_enabled = 1 // default for _, f := range strings.Split(goexperiment, ",") { if f != "" { addexp(f) @@ -51,6 +59,10 @@ func init() { } } +func Framepointer_enabled(goos, goarch string) bool { + return framepointer_enabled != 0 && goarch == "amd64" && goos != "nacl" +} + func Nopout(p *Prog) { p.As = ANOP p.Scond = 0 diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index eaf702533a141f..b6861f4c1e572c 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -664,6 +664,8 @@ type Link struct { Etextp *LSym Errors int + Framepointer_enabled bool + // state for writing objects Text []*LSym Data []*LSym diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index 6f3542b3d4f421..e974ca8c8af306 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -106,6 +106,7 @@ func Linknew(arch *LinkArch) *Link { } ctxt.Flag_optimize = true + ctxt.Framepointer_enabled = Framepointer_enabled(Getgoos(), arch.Name) return ctxt } diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 294cedcb0a25a3..18813c35a8ea3e 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -140,6 +140,11 @@ func (p *Prog) String() string { fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), Aconv(p.As), sc) sep := "\t" + quadOpAmd64 := p.RegTo2 == -1 + if quadOpAmd64 { + fmt.Fprintf(&buf, "%s$%d", sep, p.From3.Offset) + sep = ", " + } if p.From.Type != TYPE_NONE { fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From)) sep = ", " @@ -153,6 +158,8 @@ func (p *Prog) String() string { if p.From3.Type == TYPE_CONST && (p.As == ATEXT || p.As == AGLOBL) { // Special case - omit $. fmt.Fprintf(&buf, "%s%d", sep, p.From3.Offset) + } else if quadOpAmd64 { + fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.From3.Reg))) } else { fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, p.From3)) } @@ -161,7 +168,7 @@ func (p *Prog) String() string { if p.To.Type != TYPE_NONE { fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To)) } - if p.RegTo2 != REG_NONE { + if p.RegTo2 != REG_NONE && !quadOpAmd64 { fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2))) } return buf.String() diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go index f00e4afdb8f7ac..ab1dabc2b89735 100644 --- a/src/cmd/internal/obj/x86/a.out.go +++ b/src/cmd/internal/obj/x86/a.out.go @@ -785,6 +785,24 @@ const ( AVPAND AVPTEST AVPBROADCASTB + AVPSHUFB + AVPSHUFD + AVPERM2F128 + AVPALIGNR + AVPADDQ + AVPADDD + AVPSRLDQ + AVPSLLDQ + AVPSRLQ + AVPSLLQ + AVPSRLD + AVPSLLD + AVPOR + AVPBLENDD + AVINSERTI128 + AVPERM2I128 + ARORXL + ARORXQ // from 386 AJCXZW diff --git a/src/cmd/internal/obj/x86/anames.go b/src/cmd/internal/obj/x86/anames.go index e3fef54e717510..3b301546258431 100644 --- a/src/cmd/internal/obj/x86/anames.go +++ b/src/cmd/internal/obj/x86/anames.go @@ -720,6 +720,24 @@ var Anames = []string{ "VPAND", "VPTEST", "VPBROADCASTB", + "VPSHUFB", + "VPSHUFD", + "VPERM2F128", + "VPALIGNR", + "VPADDQ", + "VPADDD", + "VPSRLDQ", + "VPSLLDQ", + "VPSRLQ", + "VPSLLQ", + "VPSRLD", + "VPSLLD", + "VPOR", + "VPBLENDD", + "VINSERTI128", + "VPERM2I128", + "RORXL", + "RORXQ", "JCXZW", "FCMOVCC", "FCMOVCS", diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 8605980b94d996..414a4d34a558c5 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -208,6 +208,9 @@ const ( Zvex_rm_v_r Zvex_r_v_rm Zvex_v_rm_r + Zvex_i_rm_r + Zvex_i_r_v + Zvex_i_rm_v_r Zmax ) @@ -847,6 +850,35 @@ var yvex_xy3 = []ytab{ {Yym, Yyr, Yyr, Zvex_rm_v_r, 2}, } +var yvex_ri3 = []ytab{ + {Yi8, Ymb, Yrl, Zvex_i_rm_r, 2}, +} + +var yvex_xyi3 = []ytab{ + {Yi8, Yxm, Yxr, Zvex_i_rm_r, 2}, + {Yi8, Yym, Yyr, Zvex_i_rm_r, 2}, +} + +var yvex_yyi4 = []ytab{ //TODO don't hide 4 op, some version have xmm version + {Yym, Yyr, Yyr, Zvex_i_rm_v_r, 2}, +} + +var yvex_xyi4 = []ytab{ + {Yxm, Yyr, Yyr, Zvex_i_rm_v_r, 2}, +} + +var yvex_shift = []ytab{ + {Yi8, Yxr, Yxr, Zvex_i_r_v, 3}, + {Yi8, Yyr, Yyr, Zvex_i_r_v, 3}, + {Yxm, Yxr, Yxr, Zvex_rm_v_r, 2}, + {Yxm, Yyr, Yyr, Zvex_rm_v_r, 2}, +} + +var yvex_shift_dq = []ytab{ + {Yi8, Yxr, Yxr, Zvex_i_r_v, 3}, + {Yi8, Yyr, Yyr, Zvex_i_r_v, 3}, +} + var yvex_r3 = []ytab{ {Yml, Yrl, Yrl, Zvex_rm_v_r, 2}, {Yml, Yrl, Yrl, Zvex_rm_v_r, 2}, @@ -1679,6 +1711,24 @@ var optab = {AVPAND, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xDB, VEX_256_66_0F_WIG, 0xDB}}, {AVPBROADCASTB, yvex_vpbroadcast, Pvex, [23]uint8{VEX_128_66_0F38_W0, 0x78, VEX_256_66_0F38_W0, 0x78}}, {AVPTEST, yvex_xy2, Pvex, [23]uint8{VEX_128_66_0F38_WIG, 0x17, VEX_256_66_0F38_WIG, 0x17}}, + {AVPSHUFB, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F38_WIG, 0x00, VEX_256_66_0F38_WIG, 0x00}}, + {AVPSHUFD, yvex_xyi3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x70, VEX_256_66_0F_WIG, 0x70}}, + {AVPOR, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xeb, VEX_256_66_0F_WIG, 0xeb}}, + {AVPADDQ, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xd4, VEX_256_66_0F_WIG, 0xd4}}, + {AVPADDD, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xfe, VEX_256_66_0F_WIG, 0xfe}}, + {AVPSLLD, yvex_shift, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x72, 0xf0, VEX_256_66_0F_WIG, 0x72, 0xf0, VEX_128_66_0F_WIG, 0xf2, VEX_256_66_0F_WIG, 0xf2}}, + {AVPSLLQ, yvex_shift, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x73, 0xf0, VEX_256_66_0F_WIG, 0x73, 0xf0, VEX_128_66_0F_WIG, 0xf3, VEX_256_66_0F_WIG, 0xf3}}, + {AVPSRLD, yvex_shift, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x72, 0xd0, VEX_256_66_0F_WIG, 0x72, 0xd0, VEX_128_66_0F_WIG, 0xd2, VEX_256_66_0F_WIG, 0xd2}}, + {AVPSRLQ, yvex_shift, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x73, 0xd0, VEX_256_66_0F_WIG, 0x73, 0xd0, VEX_128_66_0F_WIG, 0xd3, VEX_256_66_0F_WIG, 0xd3}}, + {AVPSRLDQ, yvex_shift_dq, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x73, 0xd8, VEX_256_66_0F_WIG, 0x73, 0xd8}}, + {AVPSLLDQ, yvex_shift_dq, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x73, 0xf8, VEX_256_66_0F_WIG, 0x73, 0xf8}}, + {AVPERM2F128, yvex_yyi4, Pvex, [23]uint8{VEX_256_66_0F3A_W0, 0x06}}, + {AVPALIGNR, yvex_yyi4, Pvex, [23]uint8{VEX_256_66_0F3A_WIG, 0x0f}}, + {AVPBLENDD, yvex_yyi4, Pvex, [23]uint8{VEX_256_66_0F3A_WIG, 0x02}}, + {AVINSERTI128, yvex_xyi4, Pvex, [23]uint8{VEX_256_66_0F3A_WIG, 0x38}}, + {AVPERM2I128, yvex_yyi4, Pvex, [23]uint8{VEX_256_66_0F3A_WIG, 0x46}}, + {ARORXL, yvex_ri3, Pvex, [23]uint8{VEX_LZ_F2_0F3A_W0, 0xf0}}, + {ARORXQ, yvex_ri3, Pvex, [23]uint8{VEX_LZ_F2_0F3A_W1, 0xf0}}, {AXACQUIRE, ynone, Px, [23]uint8{0xf2}}, {AXRELEASE, ynone, Px, [23]uint8{0xf3}}, @@ -3189,9 +3239,16 @@ var bpduff2 = []byte{ // https://en.wikipedia.org/wiki/VEX_prefix#Technical_description func asmvex(ctxt *obj.Link, rm, v, r *obj.Addr, vex, opcode uint8) { ctxt.Vexflag = 1 - rexR := regrex[r.Reg] & Rxr - rexB := regrex[rm.Reg] & Rxb - rexX := regrex[rm.Index] & Rxx + rexR := 0 + if r != nil { + rexR = regrex[r.Reg] & Rxr + } + rexB := 0 + rexX := 0 + if rm != nil { + rexB = regrex[rm.Reg] & Rxb + rexX = regrex[rm.Index] & Rxx + } vexM := (vex >> 3) & 0xF vexWLP := vex & 0x87 vexV := byte(0) @@ -3477,6 +3534,27 @@ func doasm(ctxt *obj.Link, p *obj.Prog) { asmvex(ctxt, &p.From, p.From3, &p.To, o.op[z], o.op[z+1]) asmand(ctxt, p, &p.From, &p.To) + case Zvex_i_r_v: + asmvex(ctxt, p.From3, &p.To, nil, o.op[z], o.op[z+1]) + regnum := byte(0x7) + if p.From3.Reg >= REG_X0 && p.From3.Reg <= REG_X15 { + regnum &= byte(p.From3.Reg - REG_X0) + } else { + regnum &= byte(p.From3.Reg - REG_Y0) + } + ctxt.AsmBuf.Put1(byte(o.op[z+2]) | regnum) + ctxt.AsmBuf.Put1(byte(p.From.Offset)) + + case Zvex_i_rm_v_r: + asmvex(ctxt, &p.From, p.From3, &p.To, o.op[z], o.op[z+1]) + asmand(ctxt, p, &p.From, &p.To) + ctxt.AsmBuf.Put1(byte(p.From3.Offset)) + + case Zvex_i_rm_r: + asmvex(ctxt, p.From3, nil, &p.To, o.op[z], o.op[z+1]) + asmand(ctxt, p, p.From3, &p.To) + ctxt.AsmBuf.Put1(byte(p.From.Offset)) + case Zvex_v_rm_r: asmvex(ctxt, p.From3, &p.From, &p.To, o.op[z], o.op[z+1]) asmand(ctxt, p, p.From3, &p.To) @@ -3687,7 +3765,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) { ctxt.Diag("directly calling duff when dynamically linking Go") } - if obj.Framepointer_enabled != 0 && yt.zcase == Zcallduff && p.Mode == 64 { + if ctxt.Framepointer_enabled && yt.zcase == Zcallduff && p.Mode == 64 { // Maintain BP around call, since duffcopy/duffzero can't do it // (the call jumps into the middle of the function). // This makes it possible to see call sites for duffcopy/duffzero in @@ -3706,7 +3784,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) { r.Siz = 4 ctxt.AsmBuf.PutInt32(0) - if obj.Framepointer_enabled != 0 && yt.zcase == Zcallduff && p.Mode == 64 { + if ctxt.Framepointer_enabled && yt.zcase == Zcallduff && p.Mode == 64 { // Pop BP pushed above. // MOVQ 0(BP), BP ctxt.AsmBuf.Put(bpduff2) diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index df774437ac51dc..5dad0bbb982616 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -610,12 +610,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) { } var bpsize int - if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 { + if p.Mode == 64 && ctxt.Framepointer_enabled && autoffset > 0 && p.From3.Offset&obj.NOFRAME == 0 { // Make room for to save a base pointer. If autoffset == 0, // this might do something special like a tail jump to // another function, so in that case we omit this. bpsize = ctxt.Arch.PtrSize - autoffset += int32(bpsize) p.To.Offset += int64(bpsize) } else { diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/objfile/disasm.go index d63f8f616f189e..25c3301ab88805 100644 --- a/src/cmd/internal/objfile/disasm.go +++ b/src/cmd/internal/objfile/disasm.go @@ -15,8 +15,8 @@ import ( "strings" "text/tabwriter" - "cmd/internal/unvendor/golang.org/x/arch/arm/armasm" - "cmd/internal/unvendor/golang.org/x/arch/x86/x86asm" + "golang.org/x/arch/arm/armasm" + "golang.org/x/arch/x86/x86asm" ) // Disasm is a disassembler for a given File. diff --git a/src/cmd/internal/pprof/commands/commands.go b/src/cmd/internal/pprof/commands/commands.go index 5018c02af185e7..5dfbbd4a5dc8ac 100644 --- a/src/cmd/internal/pprof/commands/commands.go +++ b/src/cmd/internal/pprof/commands/commands.go @@ -197,6 +197,7 @@ func makeVizTmpDir() error { if err != nil { return err } + tempfile.DeferDelete(name) vizTmpDir = name return nil } diff --git a/src/cmd/internal/pprof/report/report.go b/src/cmd/internal/pprof/report/report.go index c492b752b96fbb..b11ad2ab3606e7 100644 --- a/src/cmd/internal/pprof/report/report.go +++ b/src/cmd/internal/pprof/report/report.go @@ -205,7 +205,9 @@ func nodesPerSymbol(ns nodes, symbols []*objSymbol) map[*objSymbol]nodes { // offset to adjust the sample addresses. func annotateAssembly(insns []plugin.Inst, samples nodes, base uint64) nodes { // Add end marker to simplify printing loop. - insns = append(insns, plugin.Inst{^uint64(0), "", "", 0}) + insns = append(insns, plugin.Inst{ + Addr: ^uint64(0), + }) // Ensure samples are sorted by address. samples.sort(addressOrder) diff --git a/src/cmd/internal/pprof/tempfile/tempfile.go b/src/cmd/internal/pprof/tempfile/tempfile.go index 31c117690a1c1f..a5706345e43954 100644 --- a/src/cmd/internal/pprof/tempfile/tempfile.go +++ b/src/cmd/internal/pprof/tempfile/tempfile.go @@ -27,18 +27,19 @@ func New(dir, prefix, suffix string) (*os.File, error) { var tempFiles []string var tempFilesMu = sync.Mutex{} -// DeferDelete marks a file to be deleted by next call to Cleanup() +// DeferDelete marks a file or directory to be deleted by next call to Cleanup. func DeferDelete(path string) { tempFilesMu.Lock() tempFiles = append(tempFiles, path) tempFilesMu.Unlock() } -// Cleanup removes any temporary files selected for deferred cleaning. +// Cleanup removes any temporary files or directories selected for deferred cleaning. +// Similar to defer semantics, the nodes are deleted in LIFO order. func Cleanup() { tempFilesMu.Lock() - for _, f := range tempFiles { - os.Remove(f) + for i := len(tempFiles) - 1; i >= 0; i-- { + os.Remove(tempFiles[i]) } tempFiles = nil tempFilesMu.Unlock() diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index bf1a7e74c1403f..01747c543017a7 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1558,6 +1558,12 @@ func writelines(prev *LSym) *LSym { if !haslinkregister() { offs -= int64(SysArch.PtrSize) } + if obj.Framepointer_enabled(obj.Getgoos(), obj.Getgoarch()) { + // The frame pointer is saved + // between the CFA and the + // autos. + offs -= int64(SysArch.PtrSize) + } case obj.A_PARAM: dt = DW_ABRV_PARAM diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index e66de49f413373..39d3609a291f04 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -2665,8 +2665,8 @@ func Elfadddynsym(ctxt *Link, s *LSym) { Addaddr(ctxt, d, s) } - /* size */ - Adduint32(ctxt, d, 0) + /* size of object */ + Adduint32(ctxt, d, uint32(s.Size)) /* type */ t := STB_GLOBAL << 4 diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 425c75571fee81..79cdae0aee898c 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -59,71 +59,33 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int) } data := string(bdata) - // first \n$$ marks beginning of exports - skip rest of line - p0 = strings.Index(data, "\n$$") - if p0 < 0 { - if Debug['u'] != 0 && whence != ArchiveObj { - Exitf("cannot find export data in %s", filename) - } - return - } - - // \n$$B marks the beginning of binary export data - don't skip over the B - p0 += 3 - for p0 < len(data) && data[p0] != '\n' && data[p0] != 'B' { - p0++ - } - - // second marks end of exports / beginning of local data - p1 = strings.Index(data[p0:], "\n$$\n") - if p1 < 0 && whence == Pkgdef { - p1 = len(data) - p0 - } - if p1 < 0 { - fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename) - if Debug['u'] != 0 { - errorexit() - } - return - } - p1 += p0 - - for p0 < p1 && data[p0] != 'B' && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { - p0++ - } - // don't check this section if we have binary (B) export data - // TODO fix this eventually - if p0 < p1 && data[p0] != 'B' { - if !strings.HasPrefix(data[p0:], "package ") { - fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:]) - if Debug['u'] != 0 { - errorexit() - } - return + // process header lines + isSafe := false + isMain := false + for data != "" { + var line string + if i := strings.Index(data, "\n"); i >= 0 { + line, data = data[:i], data[i+1:] + } else { + line, data = data, "" } - - p0 += 8 - for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { - p0++ + if line == "safe" { + isSafe = true } - pname := p0 - for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' { - p0++ + if line == "main" { + isMain = true } - if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) { - Exitf("load of unsafe package %s", filename) + if line == "" { + break } + } - name := data[pname:p0] - for p0 < p1 && data[p0] != '\n' { - p0++ + if whence == Pkgdef || whence == FileObj { + if pkg == "main" && !isMain { + Exitf("%s: not package main", filename) } - if p0 < p1 { - p0++ - } - - if pkg == "main" && name != "main" { - Exitf("%s: not package main (package %s)", filename, name) + if Debug['u'] != 0 && whence != ArchiveObj && !isSafe { + Exitf("load of unsafe package %s", filename) } } @@ -133,7 +95,7 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int) } // look for cgo section - p0 = strings.Index(data[p1:], "\n$$ // cgo") + p0 = strings.Index(data, "\n$$ // cgo") if p0 >= 0 { p0 += p1 i := strings.IndexByte(data[p0+1:], '\n') diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 4fff35c38e0633..bab71fb3114376 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -639,11 +639,17 @@ func loadlib() { // recording the value of GOARM. if SysArch.Family == sys.ARM { s := Linklookup(Ctxt, "runtime.goarm", 0) - s.Type = obj.SRODATA s.Size = 0 Adduint8(Ctxt, s, uint8(Ctxt.Goarm)) } + + if obj.Framepointer_enabled(obj.Getgoos(), obj.Getgoarch()) { + s := Linklookup(Ctxt, "runtime.framepointer_enabled", 0) + s.Type = obj.SRODATA + s.Size = 0 + Adduint8(Ctxt, s, 1) + } } else { // If OTOH the module does not contain the runtime package, // create a local symbol for the moduledata. @@ -1137,6 +1143,16 @@ func hostlink() { // // In both cases, switch to gold. argv = append(argv, "-fuse-ld=gold") + + // If gold is not installed, gcc will silently switch + // back to ld.bfd. So we parse the version information + // and provide a useful error if gold is missing. + cmd := exec.Command(extld, "-fuse-ld=gold", "-Wl,--version") + if out, err := cmd.CombinedOutput(); err == nil { + if !bytes.Contains(out, []byte("GNU gold")) { + log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out) + } + } } } diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index d16431ddaa29ad..be9832dc454830 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -538,7 +538,7 @@ func (r *objReader) readSymName() string { origName = make([]byte, n) r.readFull(origName) } else if err != nil { - log.Fatalf("%s: error reading symbol: %v", err) + log.Fatalf("%s: error reading symbol: %v", r.pn, err) } adjName := r.rdBuf[:0] for { diff --git a/src/cmd/pack/pack.go b/src/cmd/pack/pack.go index 5be42555d08118..1c168f946bd04e 100644 --- a/src/cmd/pack/pack.go +++ b/src/cmd/pack/pack.go @@ -286,7 +286,7 @@ func (ar *Archive) output(entry *Entry, w io.Writer) { log.Fatal("short file") } if entry.size&1 == 1 { - _, err := ar.fd.Seek(1, 1) + _, err := ar.fd.Seek(1, io.SeekCurrent) if err != nil { log.Fatal(err) } @@ -299,7 +299,7 @@ func (ar *Archive) skip(entry *Entry) { if size&1 == 1 { size++ } - _, err := ar.fd.Seek(size, 1) + _, err := ar.fd.Seek(size, io.SeekCurrent) if err != nil { log.Fatal(err) } diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index 2735bf13ea23e9..893719edbf8fdc 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -22,7 +22,9 @@ import ( "bufio" "flag" "fmt" + "html/template" "internal/trace" + "log" "net" "net/http" "os" @@ -76,20 +78,36 @@ func main() { if err != nil { dief("failed to create server socket: %v\n", err) } - // Open browser. + + log.Printf("Parsing trace...") + events, err := parseEvents() + if err != nil { + dief("%v\n", err) + } + + log.Printf("Serializing trace...") + params := &traceParams{ + events: events, + endTime: int64(1<<63 - 1), + } + data := generateTrace(params) + + log.Printf("Splitting trace...") + ranges = splitTrace(data) + + log.Printf("Opening browser") if !startBrowser("http://" + ln.Addr().String()) { fmt.Fprintf(os.Stderr, "Trace viewer is listening on http://%s\n", ln.Addr().String()) } - // Parse and symbolize trace asynchronously while browser opens. - go parseEvents() - // Start http server. http.HandleFunc("/", httpMain) err = http.Serve(ln, nil) dief("failed to start http server: %v\n", err) } +var ranges []Range + var loader struct { once sync.Once events []*trace.Event @@ -118,13 +136,23 @@ func parseEvents() ([]*trace.Event, error) { // httpMain serves the starting page. func httpMain(w http.ResponseWriter, r *http.Request) { - w.Write(templMain) + if err := templMain.Execute(w, ranges); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } -var templMain = []byte(` +var templMain = template.Must(template.New("").Parse(` -View trace
+{{if $}} + {{range $e := $}} + View trace ({{$e.Name}})
+ {{end}} +
+{{else}} + View trace
+{{end}} Goroutine analysis
Network blocking profile
Synchronization blocking profile
@@ -132,7 +160,7 @@ var templMain = []byte(` Scheduler latency profile
-`) +`)) // startBrowser tries to open the URL in a browser // and reports whether it succeeds. diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go index 7782a5efc8dd33..2b6a37bfd8dbcb 100644 --- a/src/cmd/trace/trace.go +++ b/src/cmd/trace/trace.go @@ -14,6 +14,7 @@ import ( "runtime" "strconv" "strings" + "time" ) func init() { @@ -29,17 +30,11 @@ func httpTrace(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - - params := "" - if goids := r.FormValue("goid"); goids != "" { - goid, err := strconv.ParseUint(goids, 10, 64) - if err != nil { - http.Error(w, fmt.Sprintf("failed to parse goid parameter '%v': %v", goids, err), http.StatusInternalServerError) - return - } - params = fmt.Sprintf("?goid=%v", goid) + if err := r.ParseForm(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return } - html := strings.Replace(templTrace, "{{PARAMS}}", params, -1) + html := strings.Replace(templTrace, "{{PARAMS}}", r.Form.Encode(), -1) w.Write([]byte(html)) } @@ -118,7 +113,7 @@ var templTrace = ` viewer.globalMode = true; document.body.appendChild(viewer); - url = '/jsontrace{{PARAMS}}'; + url = '/jsontrace?{{PARAMS}}'; load(); }); }()); @@ -150,6 +145,7 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) { } if goids := r.FormValue("goid"); goids != "" { + // If goid argument is present, we are rendering a trace for this particular goroutine. goid, err := strconv.ParseUint(goids, 10, 64) if err != nil { log.Printf("failed to parse goid parameter '%v': %v", goids, err) @@ -164,13 +160,81 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) { params.gs = trace.RelatedGoroutines(events, goid) } - err = json.NewEncoder(w).Encode(generateTrace(params)) + data := generateTrace(params) + + if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" { + // If start/end arguments are present, we are rendering a range of the trace. + start, err := strconv.ParseUint(startStr, 10, 64) + if err != nil { + log.Printf("failed to parse start parameter '%v': %v", startStr, err) + return + } + end, err := strconv.ParseUint(endStr, 10, 64) + if err != nil { + log.Printf("failed to parse end parameter '%v': %v", endStr, err) + return + } + if start >= uint64(len(data.Events)) || end <= start || end > uint64(len(data.Events)) { + log.Printf("bogus start/end parameters: %v/%v, trace size %v", start, end, len(data.Events)) + return + } + data.Events = append(data.Events[start:end], data.Events[data.footer:]...) + } + err = json.NewEncoder(w).Encode(data) if err != nil { log.Printf("failed to serialize trace: %v", err) return } } +type Range struct { + Name string + Start int + End int +} + +// splitTrace splits the trace into a number of ranges, +// each resulting in approx 100MB of json output (trace viewer can hardly handle more). +func splitTrace(data ViewerData) []Range { + const rangeSize = 100 << 20 + var ranges []Range + cw := new(countingWriter) + enc := json.NewEncoder(cw) + // First calculate size of the mandatory part of the trace. + // This includes stack traces and thread names. + data1 := data + data1.Events = data.Events[data.footer:] + enc.Encode(data1) + auxSize := cw.size + cw.size = 0 + // Then calculate size of each individual event and group them into ranges. + for i, start := 0, 0; i < data.footer; i++ { + enc.Encode(data.Events[i]) + if cw.size+auxSize > rangeSize || i == data.footer-1 { + ranges = append(ranges, Range{ + Name: fmt.Sprintf("%v-%v", time.Duration(data.Events[start].Time*1000), time.Duration(data.Events[i].Time*1000)), + Start: start, + End: i + 1, + }) + start = i + 1 + cw.size = 0 + } + } + if len(ranges) == 1 { + ranges = nil + } + return ranges +} + +type countingWriter struct { + size int +} + +func (cw *countingWriter) Write(data []byte) (int, error) { + cw.size += len(data) + return len(data), nil +} + type traceParams struct { events []*trace.Event gtrace bool @@ -204,6 +268,9 @@ type ViewerData struct { Events []*ViewerEvent `json:"traceEvents"` Frames map[string]ViewerFrame `json:"stackFrames"` TimeUnit string `json:"displayTimeUnit"` + + // This is where mandatory part of the trace starts (e.g. thread names) + footer int } type ViewerEvent struct { @@ -355,6 +422,7 @@ func generateTrace(params *traceParams) ViewerData { } } + ctx.data.footer = len(ctx.data.Events) ctx.emit(&ViewerEvent{Name: "process_name", Phase: "M", Pid: 0, Arg: &NameArg{"PROCS"}}) ctx.emit(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: 0, Arg: &SortIndexArg{1}}) diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/Makefile b/src/cmd/vendor/golang.org/x/arch/arm/armasm/Makefile similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/Makefile rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/Makefile diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/decode.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/decode.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode_test.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/decode_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/decode_test.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/decode_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/ext_test.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/ext_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/ext_test.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/ext_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/gnu.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/gnu.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/gnu.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/gnu.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/inst.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/inst.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/inst.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/inst.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdump_test.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/objdump_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdump_test.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/objdump_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdumpext_test.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/objdumpext_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/objdumpext_test.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/objdumpext_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/plan9x.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/plan9x.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/plan9x.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/plan9x.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/tables.go b/src/cmd/vendor/golang.org/x/arch/arm/armasm/tables.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/tables.go rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/tables.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/Makefile b/src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/Makefile similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/Makefile rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/Makefile diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/decode.txt b/src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/decode.txt similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/arm/armasm/testdata/decode.txt rename to src/cmd/vendor/golang.org/x/arch/arm/armasm/testdata/decode.txt diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/Makefile b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/Makefile similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/Makefile rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/Makefile diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/decode_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/decode_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/ext_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/ext_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/ext_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/gnu.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/gnu.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/gnu.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/gnu.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/inst_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/inst_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/intel.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/intel.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/intel.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/intel.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdump_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdump_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdump_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdump_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/plan9x_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/plan9x_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/tables.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/tables.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/Makefile b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/Makefile similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/Makefile rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/Makefile diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xed_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/xed_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xed_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/xed_test.go diff --git a/src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xedext_test.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/xedext_test.go similarity index 100% rename from src/cmd/internal/unvendor/golang.org/x/arch/x86/x86asm/xedext_test.go rename to src/cmd/vendor/golang.org/x/arch/x86/x86asm/xedext_test.go diff --git a/src/cmd/internal/unvendor/vendor.json b/src/cmd/vendor/vendor.json similarity index 100% rename from src/cmd/internal/unvendor/vendor.json rename to src/cmd/vendor/vendor.json diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go index 07499e6ae6bc6a..f4b985cfbdc461 100644 --- a/src/cmd/vet/print.go +++ b/src/cmd/vet/print.go @@ -587,22 +587,24 @@ func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, s func (f *File) checkPrint(call *ast.CallExpr, name string) { firstArg := 0 typ := f.pkg.types[call.Fun].Type - if typ != nil { - if sig, ok := typ.(*types.Signature); ok { - if !sig.Variadic() { - // Skip checking non-variadic functions. - return - } - params := sig.Params() - firstArg = params.Len() - 1 - - typ := params.At(firstArg).Type() - typ = typ.(*types.Slice).Elem() - it, ok := typ.(*types.Interface) - if !ok || !it.Empty() { - // Skip variadic functions accepting non-interface{} args. - return - } + if typ == nil { + // Skip checking functions with unknown type. + return + } + if sig, ok := typ.(*types.Signature); ok { + if !sig.Variadic() { + // Skip checking non-variadic functions. + return + } + params := sig.Params() + firstArg = params.Len() - 1 + + typ := params.At(firstArg).Type() + typ = typ.(*types.Slice).Elem() + it, ok := typ.(*types.Interface) + if !ok || !it.Empty() { + // Skip variadic functions accepting non-interface{} args. + return } } args := call.Args diff --git a/src/cmd/vet/testdata/copylock.go b/src/cmd/vet/testdata/copylock.go index cf56802cdbb249..d49f4686275871 100644 --- a/src/cmd/vet/testdata/copylock.go +++ b/src/cmd/vet/testdata/copylock.go @@ -1,6 +1,9 @@ package testdata -import "sync" +import ( + "sync" + "sync/atomic" +) func OkFunc() { var x *sync.Mutex @@ -66,3 +69,72 @@ func BadFunc() { new := func(interface{}) {} new(t) // ERROR "function call copies lock value: testdata.Tlock contains sync.Once contains sync.Mutex" } + +// SyncTypesCheck checks copying of sync.* types except sync.Mutex +func SyncTypesCheck() { + // sync.RWMutex copying + var rwmuX sync.RWMutex + var rwmuXX = sync.RWMutex{} + rwmuX1 := new(sync.RWMutex) + rwmuY := rwmuX // ERROR "assignment copies lock value to rwmuY: sync.RWMutex" + rwmuY = rwmuX // ERROR "assignment copies lock value to rwmuY: sync.RWMutex" + var rwmuYY = rwmuX // ERROR "variable declaration copies lock value to rwmuYY: sync.RWMutex" + rwmuP := &rwmuX + rwmuZ := &sync.RWMutex{} + + // sync.Cond copying + var condX sync.Cond + var condXX = sync.Cond{} + condX1 := new(sync.Cond) + condY := condX // ERROR "assignment copies lock value to condY: sync.Cond contains sync.noCopy" + condY = condX // ERROR "assignment copies lock value to condY: sync.Cond contains sync.noCopy" + var condYY = condX // ERROR "variable declaration copies lock value to condYY: sync.Cond contains sync.noCopy" + condP := &condX + condZ := &sync.Cond{ + L: &sync.Mutex{}, + } + condZ = sync.NewCond(&sync.Mutex{}) + + // sync.WaitGroup copying + var wgX sync.WaitGroup + var wgXX = sync.WaitGroup{} + wgX1 := new(sync.WaitGroup) + wgY := wgX // ERROR "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy" + wgY = wgX // ERROR "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy" + var wgYY = wgX // ERROR "variable declaration copies lock value to wgYY: sync.WaitGroup contains sync.noCopy" + wgP := &wgX + wgZ := &sync.WaitGroup{} + + // sync.Pool copying + var poolX sync.Pool + var poolXX = sync.Pool{} + poolX1 := new(sync.Pool) + poolY := poolX // ERROR "assignment copies lock value to poolY: sync.Pool contains sync.noCopy" + poolY = poolX // ERROR "assignment copies lock value to poolY: sync.Pool contains sync.noCopy" + var poolYY = poolX // ERROR "variable declaration copies lock value to poolYY: sync.Pool contains sync.noCopy" + poolP := &poolX + poolZ := &sync.Pool{} + + // sync.Once copying + var onceX sync.Once + var onceXX = sync.Once{} + onceX1 := new(sync.Once) + onceY := onceX // ERROR "assignment copies lock value to onceY: sync.Once contains sync.Mutex" + onceY = onceX // ERROR "assignment copies lock value to onceY: sync.Once contains sync.Mutex" + var onceYY = onceX // ERROR "variable declaration copies lock value to onceYY: sync.Once contains sync.Mutex" + onceP := &onceX + onceZ := &sync.Once{} +} + +// AtomicTypesCheck checks copying of sync/atomic types +func AtomicTypesCheck() { + // atomic.Value copying + var vX atomic.Value + var vXX = atomic.Value{} + vX1 := new(atomic.Value) + vY := vX // ERROR "assignment copies lock value to vY: sync/atomic.Value contains sync/atomic.noCopy" + vY = vX // ERROR "assignment copies lock value to vY: sync/atomic.Value contains sync/atomic.noCopy" + var vYY = vX // ERROR "variable declaration copies lock value to vYY: sync/atomic.Value contains sync/atomic.noCopy" + vP := &vX + vZ := &atomic.Value{} +} diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go index 261ee788c7b0d0..ab97256c088298 100644 --- a/src/cmd/vet/testdata/print.go +++ b/src/cmd/vet/testdata/print.go @@ -8,6 +8,7 @@ package testdata import ( "fmt" + "io" "math" "os" "unsafe" // just for test case printing unsafe.Pointer @@ -272,11 +273,21 @@ func Printf(format string, args ...interface{}) { panic("don't call - testing only") } +// Println is used by the test so we must declare it. +func Println(args ...interface{}) { + panic("don't call - testing only") +} + // Logf is used by the test so we must declare it. func Logf(format string, args ...interface{}) { panic("don't call - testing only") } +// Log is used by the test so we must declare it. +func Log(args ...interface{}) { + panic("don't call - testing only") +} + // printf is used by the test so we must declare it. func printf(format string, args ...interface{}) { panic("don't call - testing only") @@ -415,3 +426,10 @@ var recursiveStruct1V = &RecursiveStruct1{} func (int) String() { return "" } + +func (s *unknownStruct) Fprintln(w io.Writer, s string) {} + +func UnknownStructFprintln() { + s := unknownStruct{} + s.Fprintln(os.Stdout, "hello, world!") // OK +} diff --git a/src/compress/flate/reader_test.go b/src/compress/flate/reader_test.go index b336278c07d015..b0a16ce18b98d6 100644 --- a/src/compress/flate/reader_test.go +++ b/src/compress/flate/reader_test.go @@ -22,82 +22,77 @@ func TestNlitOutOfRange(t *testing.T) { "\x75\xc4\xf8\x0f\x12\x11\xb9\xb4\x4b\x09\xa0\xbe\x8b\x91\x4c"))) } -const ( - digits = iota - twain -) - -var testfiles = []string{ +var suites = []struct{ name, file string }{ // Digits is the digits of the irrational number e. Its decimal representation // does not repeat, but there are only 10 possible digits, so it should be // reasonably compressible. - digits: "../testdata/e.txt", + {"Digits", "../testdata/e.txt"}, // Twain is Mark Twain's classic English novel. - twain: "../testdata/Mark.Twain-Tom.Sawyer.txt", + {"Twain", "../testdata/Mark.Twain-Tom.Sawyer.txt"}, } -func benchmarkDecode(b *testing.B, testfile, level, n int) { - b.ReportAllocs() - b.StopTimer() - b.SetBytes(int64(n)) - buf0, err := ioutil.ReadFile(testfiles[testfile]) - if err != nil { - b.Fatal(err) - } - if len(buf0) == 0 { - b.Fatalf("test file %q has no data", testfiles[testfile]) - } - compressed := new(bytes.Buffer) - w, err := NewWriter(compressed, level) - if err != nil { - b.Fatal(err) - } - for i := 0; i < n; i += len(buf0) { - if len(buf0) > n-i { - buf0 = buf0[:n-i] +func BenchmarkDecode(b *testing.B) { + doBench(b, func(b *testing.B, buf0 []byte, level, n int) { + b.ReportAllocs() + b.StopTimer() + b.SetBytes(int64(n)) + + compressed := new(bytes.Buffer) + w, err := NewWriter(compressed, level) + if err != nil { + b.Fatal(err) } - io.Copy(w, bytes.NewReader(buf0)) - } - w.Close() - buf1 := compressed.Bytes() - buf0, compressed, w = nil, nil, nil - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1))) - } + for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } + io.Copy(w, bytes.NewReader(buf0)) + } + w.Close() + buf1 := compressed.Bytes() + buf0, compressed, w = nil, nil, nil + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1))) + } + }) } -// These short names are so that gofmt doesn't break the BenchmarkXxx function -// bodies below over multiple lines. -const ( - speed = BestSpeed - default_ = DefaultCompression - compress = BestCompression - huffman = HuffmanOnly -) +var levelTests = []struct { + name string + level int +}{ + {"Huffman", HuffmanOnly}, + {"Speed", BestSpeed}, + {"Default", DefaultCompression}, + {"Compression", BestCompression}, +} -func BenchmarkDecodeDigitsHuffman1e4(b *testing.B) { benchmarkDecode(b, digits, huffman, 1e4) } -func BenchmarkDecodeDigitsHuffman1e5(b *testing.B) { benchmarkDecode(b, digits, huffman, 1e5) } -func BenchmarkDecodeDigitsHuffman1e6(b *testing.B) { benchmarkDecode(b, digits, huffman, 1e6) } -func BenchmarkDecodeDigitsSpeed1e4(b *testing.B) { benchmarkDecode(b, digits, speed, 1e4) } -func BenchmarkDecodeDigitsSpeed1e5(b *testing.B) { benchmarkDecode(b, digits, speed, 1e5) } -func BenchmarkDecodeDigitsSpeed1e6(b *testing.B) { benchmarkDecode(b, digits, speed, 1e6) } -func BenchmarkDecodeDigitsDefault1e4(b *testing.B) { benchmarkDecode(b, digits, default_, 1e4) } -func BenchmarkDecodeDigitsDefault1e5(b *testing.B) { benchmarkDecode(b, digits, default_, 1e5) } -func BenchmarkDecodeDigitsDefault1e6(b *testing.B) { benchmarkDecode(b, digits, default_, 1e6) } -func BenchmarkDecodeDigitsCompress1e4(b *testing.B) { benchmarkDecode(b, digits, compress, 1e4) } -func BenchmarkDecodeDigitsCompress1e5(b *testing.B) { benchmarkDecode(b, digits, compress, 1e5) } -func BenchmarkDecodeDigitsCompress1e6(b *testing.B) { benchmarkDecode(b, digits, compress, 1e6) } -func BenchmarkDecodeTwainHuffman1e4(b *testing.B) { benchmarkDecode(b, twain, huffman, 1e4) } -func BenchmarkDecodeTwainHuffman1e5(b *testing.B) { benchmarkDecode(b, twain, huffman, 1e5) } -func BenchmarkDecodeTwainHuffman1e6(b *testing.B) { benchmarkDecode(b, twain, huffman, 1e6) } -func BenchmarkDecodeTwainSpeed1e4(b *testing.B) { benchmarkDecode(b, twain, speed, 1e4) } -func BenchmarkDecodeTwainSpeed1e5(b *testing.B) { benchmarkDecode(b, twain, speed, 1e5) } -func BenchmarkDecodeTwainSpeed1e6(b *testing.B) { benchmarkDecode(b, twain, speed, 1e6) } -func BenchmarkDecodeTwainDefault1e4(b *testing.B) { benchmarkDecode(b, twain, default_, 1e4) } -func BenchmarkDecodeTwainDefault1e5(b *testing.B) { benchmarkDecode(b, twain, default_, 1e5) } -func BenchmarkDecodeTwainDefault1e6(b *testing.B) { benchmarkDecode(b, twain, default_, 1e6) } -func BenchmarkDecodeTwainCompress1e4(b *testing.B) { benchmarkDecode(b, twain, compress, 1e4) } -func BenchmarkDecodeTwainCompress1e5(b *testing.B) { benchmarkDecode(b, twain, compress, 1e5) } -func BenchmarkDecodeTwainCompress1e6(b *testing.B) { benchmarkDecode(b, twain, compress, 1e6) } +var sizes = []struct { + name string + n int +}{ + {"1e4", 1e4}, + {"1e5", 1e5}, + {"1e6", 1e6}, +} + +func doBench(b *testing.B, f func(b *testing.B, buf []byte, level, n int)) { + for _, suite := range suites { + buf, err := ioutil.ReadFile(suite.file) + if err != nil { + b.Fatal(err) + } + if len(buf) == 0 { + b.Fatalf("test file %q has no data", suite.file) + } + for _, l := range levelTests { + for _, s := range sizes { + b.Run(suite.name+"/"+l.name+"/"+s.name, func(b *testing.B) { + f(b, buf, l.level, s.n) + }) + } + } + } +} diff --git a/src/compress/flate/writer_test.go b/src/compress/flate/writer_test.go index 7967cd739c5862..21cd0b22eef5fe 100644 --- a/src/compress/flate/writer_test.go +++ b/src/compress/flate/writer_test.go @@ -14,62 +14,33 @@ import ( "testing" ) -func benchmarkEncoder(b *testing.B, testfile, level, n int) { - b.StopTimer() - b.SetBytes(int64(n)) - buf0, err := ioutil.ReadFile(testfiles[testfile]) - if err != nil { - b.Fatal(err) - } - if len(buf0) == 0 { - b.Fatalf("test file %q has no data", testfiles[testfile]) - } - buf1 := make([]byte, n) - for i := 0; i < n; i += len(buf0) { - if len(buf0) > n-i { - buf0 = buf0[:n-i] +func BenchmarkEncode(b *testing.B) { + doBench(b, func(b *testing.B, buf0 []byte, level, n int) { + b.StopTimer() + b.SetBytes(int64(n)) + + buf1 := make([]byte, n) + for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } + copy(buf1[i:], buf0) } - copy(buf1[i:], buf0) - } - buf0 = nil - w, err := NewWriter(ioutil.Discard, level) - if err != nil { - b.Fatal(err) - } - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - w.Reset(ioutil.Discard) - w.Write(buf1) - w.Close() - } + buf0 = nil + w, err := NewWriter(ioutil.Discard, level) + if err != nil { + b.Fatal(err) + } + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + w.Reset(ioutil.Discard) + w.Write(buf1) + w.Close() + } + }) } -func BenchmarkEncodeDigitsHuffman1e4(b *testing.B) { benchmarkEncoder(b, digits, huffman, 1e4) } -func BenchmarkEncodeDigitsHuffman1e5(b *testing.B) { benchmarkEncoder(b, digits, huffman, 1e5) } -func BenchmarkEncodeDigitsHuffman1e6(b *testing.B) { benchmarkEncoder(b, digits, huffman, 1e6) } -func BenchmarkEncodeDigitsSpeed1e4(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e4) } -func BenchmarkEncodeDigitsSpeed1e5(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e5) } -func BenchmarkEncodeDigitsSpeed1e6(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e6) } -func BenchmarkEncodeDigitsDefault1e4(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e4) } -func BenchmarkEncodeDigitsDefault1e5(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e5) } -func BenchmarkEncodeDigitsDefault1e6(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e6) } -func BenchmarkEncodeDigitsCompress1e4(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e4) } -func BenchmarkEncodeDigitsCompress1e5(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e5) } -func BenchmarkEncodeDigitsCompress1e6(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e6) } -func BenchmarkEncodeTwainHuffman1e4(b *testing.B) { benchmarkEncoder(b, twain, huffman, 1e4) } -func BenchmarkEncodeTwainHuffman1e5(b *testing.B) { benchmarkEncoder(b, twain, huffman, 1e5) } -func BenchmarkEncodeTwainHuffman1e6(b *testing.B) { benchmarkEncoder(b, twain, huffman, 1e6) } -func BenchmarkEncodeTwainSpeed1e4(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e4) } -func BenchmarkEncodeTwainSpeed1e5(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e5) } -func BenchmarkEncodeTwainSpeed1e6(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e6) } -func BenchmarkEncodeTwainDefault1e4(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e4) } -func BenchmarkEncodeTwainDefault1e5(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e5) } -func BenchmarkEncodeTwainDefault1e6(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e6) } -func BenchmarkEncodeTwainCompress1e4(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e4) } -func BenchmarkEncodeTwainCompress1e5(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e5) } -func BenchmarkEncodeTwainCompress1e6(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e6) } - // errorWriter is a writer that fails after N writes. type errorWriter struct { N int @@ -141,17 +112,12 @@ func TestWriteError(t *testing.T) { // Test if two runs produce identical results // even when writing different sizes to the Writer. -func TestDeterministicL0(t *testing.T) { testDeterministic(0, t) } -func TestDeterministicL1(t *testing.T) { testDeterministic(1, t) } -func TestDeterministicL2(t *testing.T) { testDeterministic(2, t) } -func TestDeterministicL3(t *testing.T) { testDeterministic(3, t) } -func TestDeterministicL4(t *testing.T) { testDeterministic(4, t) } -func TestDeterministicL5(t *testing.T) { testDeterministic(5, t) } -func TestDeterministicL6(t *testing.T) { testDeterministic(6, t) } -func TestDeterministicL7(t *testing.T) { testDeterministic(7, t) } -func TestDeterministicL8(t *testing.T) { testDeterministic(8, t) } -func TestDeterministicL9(t *testing.T) { testDeterministic(9, t) } -func TestDeterministicLM2(t *testing.T) { testDeterministic(-2, t) } +func TestDeterministic(t *testing.T) { + for i := 0; i <= 9; i++ { + t.Run(fmt.Sprint("L", i), func(t *testing.T) { testDeterministic(i, t) }) + } + t.Run("LM2", func(t *testing.T) { testDeterministic(-2, t) }) +} func testDeterministic(i int, t *testing.T) { // Test so much we cross a good number of block boundaries. diff --git a/src/compress/gzip/gunzip.go b/src/compress/gzip/gunzip.go index 926bae88c70bda..7e640692f3f265 100644 --- a/src/compress/gzip/gunzip.go +++ b/src/compress/gzip/gunzip.go @@ -282,4 +282,6 @@ func (z *Reader) Read(p []byte) (n int, err error) { } // Close closes the Reader. It does not close the underlying io.Reader. +// In order for the GZIP checksum to be verified, the reader must be +// fully consumed until the io.EOF. func (z *Reader) Close() error { return z.decompressor.Close() } diff --git a/src/compress/lzw/reader_test.go b/src/compress/lzw/reader_test.go index c3a5c3a0aaacdf..6b9f9a3da7035d 100644 --- a/src/compress/lzw/reader_test.go +++ b/src/compress/lzw/reader_test.go @@ -6,8 +6,10 @@ package lzw import ( "bytes" + "fmt" "io" "io/ioutil" + "math" "runtime" "strconv" "strings" @@ -118,42 +120,37 @@ func TestReader(t *testing.T) { } } -func benchmarkDecoder(b *testing.B, n int) { - b.StopTimer() - b.SetBytes(int64(n)) - buf0, err := ioutil.ReadFile("../testdata/e.txt") +func BenchmarkDecoder(b *testing.B) { + buf, err := ioutil.ReadFile("../testdata/e.txt") if err != nil { b.Fatal(err) } - if len(buf0) == 0 { + if len(buf) == 0 { b.Fatalf("test file has no data") } - compressed := new(bytes.Buffer) - w := NewWriter(compressed, LSB, 8) - for i := 0; i < n; i += len(buf0) { - if len(buf0) > n-i { - buf0 = buf0[:n-i] - } - w.Write(buf0) - } - w.Close() - buf1 := compressed.Bytes() - buf0, compressed, w = nil, nil, nil - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1), LSB, 8)) - } -} - -func BenchmarkDecoder1e4(b *testing.B) { - benchmarkDecoder(b, 1e4) -} -func BenchmarkDecoder1e5(b *testing.B) { - benchmarkDecoder(b, 1e5) -} - -func BenchmarkDecoder1e6(b *testing.B) { - benchmarkDecoder(b, 1e6) + for e := 4; e <= 6; e++ { + n := int(math.Pow10(e)) + b.Run(fmt.Sprint("1e", e), func(b *testing.B) { + b.StopTimer() + b.SetBytes(int64(n)) + buf0 := buf + compressed := new(bytes.Buffer) + w := NewWriter(compressed, LSB, 8) + for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } + w.Write(buf0) + } + w.Close() + buf1 := compressed.Bytes() + buf0, compressed, w = nil, nil, nil + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1), LSB, 8)) + } + }) + } } diff --git a/src/compress/lzw/writer_test.go b/src/compress/lzw/writer_test.go index 66d761727f481d..4979f8b3521139 100644 --- a/src/compress/lzw/writer_test.go +++ b/src/compress/lzw/writer_test.go @@ -5,9 +5,11 @@ package lzw import ( + "fmt" "internal/testenv" "io" "io/ioutil" + "math" "os" "runtime" "testing" @@ -122,41 +124,34 @@ func TestSmallLitWidth(t *testing.T) { } } -func benchmarkEncoder(b *testing.B, n int) { - b.StopTimer() - b.SetBytes(int64(n)) - buf0, err := ioutil.ReadFile("../testdata/e.txt") +func BenchmarkEncoder(b *testing.B) { + buf, err := ioutil.ReadFile("../testdata/e.txt") if err != nil { b.Fatal(err) } - if len(buf0) == 0 { + if len(buf) == 0 { b.Fatalf("test file has no data") } - buf1 := make([]byte, n) - for i := 0; i < n; i += len(buf0) { - if len(buf0) > n-i { - buf0 = buf0[:n-i] + + for e := 4; e <= 6; e++ { + n := int(math.Pow10(e)) + buf0 := buf + buf1 := make([]byte, n) + for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } + copy(buf1[i:], buf0) } - copy(buf1[i:], buf0) - } - buf0 = nil - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - w := NewWriter(ioutil.Discard, LSB, 8) - w.Write(buf1) - w.Close() + buf0 = nil + runtime.GC() + b.Run(fmt.Sprint("1e", e), func(b *testing.B) { + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + w := NewWriter(ioutil.Discard, LSB, 8) + w.Write(buf1) + w.Close() + } + }) } } - -func BenchmarkEncoder1e4(b *testing.B) { - benchmarkEncoder(b, 1e4) -} - -func BenchmarkEncoder1e5(b *testing.B) { - benchmarkEncoder(b, 1e5) -} - -func BenchmarkEncoder1e6(b *testing.B) { - benchmarkEncoder(b, 1e6) -} diff --git a/src/compress/zlib/reader.go b/src/compress/zlib/reader.go index 30535fd980e55b..2efa1930354585 100644 --- a/src/compress/zlib/reader.go +++ b/src/compress/zlib/reader.go @@ -62,7 +62,8 @@ type Resetter interface { // NewReader creates a new ReadCloser. // Reads from the returned ReadCloser read and decompress data from r. -// The implementation buffers input and may read more data than necessary from r. +// If r does not implement io.ByteReader, the decompressor may read more +// data than necessary from r. // It is the caller's responsibility to call Close on the ReadCloser when done. // // The ReadCloser returned by NewReader also implements Resetter. @@ -115,6 +116,8 @@ func (z *reader) Read(p []byte) (int, error) { } // Calling Close does not close the wrapped io.Reader originally passed to NewReader. +// In order for the ZLIB checksum to be verified, the reader must be +// fully consumed until the io.EOF. func (z *reader) Close() error { if z.err != nil && z.err != io.EOF { return z.err diff --git a/src/container/list/list_test.go b/src/container/list/list_test.go index 4d8bfc2bf0791b..e3bfe53a4981fa 100644 --- a/src/container/list/list_test.go +++ b/src/container/list/list_test.go @@ -326,7 +326,7 @@ func TestInsertAfterUnknownMark(t *testing.T) { } // Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l. -func TestMoveUnkownMark(t *testing.T) { +func TestMoveUnknownMark(t *testing.T) { var l1 List e1 := l1.PushBack(1) diff --git a/src/context/context.go b/src/context/context.go index 9ff19503b203ad..169db74f57ffe0 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -144,7 +144,13 @@ var Canceled = errors.New("context canceled") // DeadlineExceeded is the error returned by Context.Err when the context's // deadline passes. -var DeadlineExceeded = errors.New("context deadline exceeded") +var DeadlineExceeded error = deadlineExceededError{} + +type deadlineExceededError struct{} + +func (deadlineExceededError) Error() string { return "context deadline exceeded" } + +func (deadlineExceededError) Timeout() bool { return true } // An emptyCtx is never canceled, has no values, and has no deadline. It is not // struct{}, since vars of this type must have distinct addresses. diff --git a/src/context/context_test.go b/src/context/context_test.go index 99456b188d2a65..90e78e57ecc42e 100644 --- a/src/context/context_test.go +++ b/src/context/context_test.go @@ -594,3 +594,15 @@ func recoveredValue(fn func()) (v interface{}) { fn() return } + +func TestDeadlineExceededSupportsTimeout(t *testing.T) { + i, ok := DeadlineExceeded.(interface { + Timeout() bool + }) + if !ok { + t.Fatal("DeadlineExceeded does not support Timeout interface") + } + if !i.Timeout() { + t.Fatal("wrong value for timeout") + } +} diff --git a/src/crypto/cipher/example_test.go b/src/crypto/cipher/example_test.go index f6cc38650610ad..9abe782bca54ae 100644 --- a/src/crypto/cipher/example_test.go +++ b/src/crypto/cipher/example_test.go @@ -44,9 +44,9 @@ func ExampleNewGCMDecrypter() { // The key argument should be the AES key, either 16 or 32 bytes // to select AES-128 or AES-256. key := []byte("AES256Key-32Characters1234567890") - ciphertext, _ := hex.DecodeString("f90fbef747e7212ad7410d0eee2d965de7e890471695cddd2a5bc0ef5da1d04ad8147b62141ad6e4914aee8c512f64fba9037603d41de0d50b718bd665f019cdcd") + ciphertext, _ := hex.DecodeString("1019aa66cd7c024f9efd0038899dae1973ee69427f5a6579eba292ffe1b5a260") - nonce, _ := hex.DecodeString("bb8ef84243d2ee95a41c6c57") + nonce, _ := hex.DecodeString("37b8e8a308c354048d245f6d") block, err := aes.NewCipher(key) if err != nil { @@ -63,7 +63,8 @@ func ExampleNewGCMDecrypter() { panic(err.Error()) } - fmt.Printf("%s\n", string(plaintext)) + fmt.Printf("%s\n", plaintext) + // Output: exampleplaintext } func ExampleNewCBCDecrypter() { diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index e63bd8669ef443..288e366a882de2 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -228,7 +228,7 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { c := pub.Curve N := c.Params().N - if r.Sign() == 0 || s.Sign() == 0 { + if r.Sign() <= 0 || s.Sign() <= 0 { return false } if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 { diff --git a/src/crypto/ecdsa/ecdsa_test.go b/src/crypto/ecdsa/ecdsa_test.go index 5e588b92582bbc..fc25fd74a78ce7 100644 --- a/src/crypto/ecdsa/ecdsa_test.go +++ b/src/crypto/ecdsa/ecdsa_test.go @@ -296,3 +296,26 @@ func TestVectors(t *testing.T) { } } } + +func testNegativeInputs(t *testing.T, curve elliptic.Curve, tag string) { + key, err := GenerateKey(curve, rand.Reader) + if err != nil { + t.Errorf("failed to generate key for %q", tag) + } + + var hash [32]byte + r := new(big.Int).SetInt64(1) + r.Lsh(r, 550 /* larger than any supported curve */) + r.Neg(r) + + if Verify(&key.PublicKey, hash[:], r, r) { + t.Errorf("bogus signature accepted for %q", tag) + } +} + +func TestNegativeInputs(t *testing.T) { + testNegativeInputs(t, elliptic.P224(), "p224") + testNegativeInputs(t, elliptic.P256(), "p256") + testNegativeInputs(t, elliptic.P384(), "p384") + testNegativeInputs(t, elliptic.P521(), "p521") +} diff --git a/src/crypto/elliptic/p256_amd64.go b/src/crypto/elliptic/p256_amd64.go index e96933e0c55667..66b7cf8dc512a4 100644 --- a/src/crypto/elliptic/p256_amd64.go +++ b/src/crypto/elliptic/p256_amd64.go @@ -93,10 +93,14 @@ func p256PointAddAsm(res, in1, in2 []uint64) func p256PointDoubleAsm(res, in []uint64) func (curve p256Curve) Inverse(k *big.Int) *big.Int { + if k.Sign() < 0 { + // This should never happen. + k = new(big.Int).Neg(k) + } + if k.Cmp(p256.N) >= 0 { // This should never happen. - reducedK := new(big.Int).Mod(k, p256.N) - k = reducedK + k = new(big.Int).Mod(k, p256.N) } // table will store precomputed powers of x. The four words at index diff --git a/src/crypto/sha1/issue15617_test.go b/src/crypto/sha1/issue15617_test.go new file mode 100644 index 00000000000000..98038e58077c2f --- /dev/null +++ b/src/crypto/sha1/issue15617_test.go @@ -0,0 +1,28 @@ +// +build amd64 +// +build linux darwin + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha1_test + +import ( + "crypto/sha1" + "syscall" + "testing" +) + +func TestOutOfBoundsRead(t *testing.T) { + const pageSize = 4 << 10 + data, err := syscall.Mmap(0, 0, 2*pageSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE) + if err != nil { + panic(err) + } + if err := syscall.Mprotect(data[pageSize:], syscall.PROT_NONE); err != nil { + panic(err) + } + for i := 0; i < pageSize; i++ { + sha1.Sum(data[pageSize-i : pageSize]) + } +} diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go index 9202e682a8f9b9..214afc51e1fa64 100644 --- a/src/crypto/sha1/sha1_test.go +++ b/src/crypto/sha1/sha1_test.go @@ -19,6 +19,7 @@ type sha1Test struct { } var golden = []sha1Test{ + {"76245dbf96f661bd221046197ab8b9f063f11bad", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"}, {"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""}, {"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"}, {"da23614e02469a0d7c7bd1bdab5c9c474b1904dc", "ab"}, @@ -93,13 +94,15 @@ func TestBlockSize(t *testing.T) { // Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match. func TestBlockGeneric(t *testing.T) { - gen, asm := New().(*digest), New().(*digest) - buf := make([]byte, BlockSize*20) // arbitrary factor - rand.Read(buf) - blockGeneric(gen, buf) - block(asm, buf) - if *gen != *asm { - t.Error("block and blockGeneric resulted in different states") + for i := 1; i < 30; i++ { // arbitrary factor + gen, asm := New().(*digest), New().(*digest) + buf := make([]byte, BlockSize*i) + rand.Read(buf) + blockGeneric(gen, buf) + block(asm, buf) + if *gen != *asm { + t.Errorf("For %#v block and blockGeneric resulted in different states", buf) + } } } @@ -120,6 +123,10 @@ func BenchmarkHash8Bytes(b *testing.B) { benchmarkSize(b, 8) } +func BenchmarkHash320Bytes(b *testing.B) { + benchmarkSize(b, 320) +} + func BenchmarkHash1K(b *testing.B) { benchmarkSize(b, 1024) } diff --git a/src/crypto/sha1/sha1block_amd64.go b/src/crypto/sha1/sha1block_amd64.go new file mode 100644 index 00000000000000..fd85a4262be21a --- /dev/null +++ b/src/crypto/sha1/sha1block_amd64.go @@ -0,0 +1,34 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha1 + +//go:noescape + +func blockAVX2(dig *digest, p []byte) + +//go:noescape +func blockAMD64(dig *digest, p []byte) +func checkAVX2() bool + +var hasAVX2 = checkAVX2() + +func block(dig *digest, p []byte) { + if hasAVX2 && len(p) >= 256 { + // blockAVX2 calculates sha1 for 2 block per iteration + // it also interleaves precalculation for next block. + // So it may read up-to 192 bytes past end of p + // We may add checks inside blockAVX2, but this will + // just turn it into a copy of blockAMD64, + // so call it directly, instead. + safeLen := len(p) - 128 + if safeLen%128 != 0 { + safeLen -= 64 + } + blockAVX2(dig, p[:safeLen]) + blockAMD64(dig, p[safeLen:]) + } else { + blockAMD64(dig, p) + } +} diff --git a/src/crypto/sha1/sha1block_amd64.s b/src/crypto/sha1/sha1block_amd64.s index a504e147512c73..0cdb43b422fdcc 100644 --- a/src/crypto/sha1/sha1block_amd64.s +++ b/src/crypto/sha1/sha1block_amd64.s @@ -2,6 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// AVX2 version by Intel, same algorithm as code in Linux kernel: +// https://github.com/torvalds/linux/blob/master/arch/x86/crypto/sha1_avx2_x86_64_asm.S +// Authors: +// Ilya Albrekht +// Maxim Locktyukhin +// Ronen Zohar +// Chandramouli Narayanan + + #include "textflag.h" // SHA1 block routine. See sha1block.go for Go equivalent. @@ -87,7 +96,7 @@ FUNC4(a, b, c, d, e); \ MIX(a, b, c, d, e, 0xCA62C1D6) -TEXT ·block(SB),NOSPLIT,$64-32 +TEXT ·blockAMD64(SB),NOSPLIT,$64-32 MOVQ dig+0(FP), BP MOVQ p_base+8(FP), SI MOVQ p_len+16(FP), DX @@ -214,3 +223,1293 @@ end: MOVL DX, (3*4)(DI) MOVL BP, (4*4)(DI) RET + + +// This is the implementation using AVX2. It is based on: +// "SHA-1 implementation with Intel(R) AVX2 instruction set extensions" +// From http://software.intel.com/en-us/articles +// (look for improving-the-performance-of-the-secure-hash-algorithm-1) +// This implementation is 2x unrolled, and interleaves vector instructions, +// used to precompute W, with scalar computation of current round +// for optimal scheduling. + +// Trivial helper macros. +#define UPDATE_HASH(A,TB,C,D,E) \ + ADDL (R9), A \ + MOVL A, (R9) \ + ADDL 4(R9), TB \ + MOVL TB, 4(R9) \ + ADDL 8(R9), C \ + MOVL C, 8(R9) \ + ADDL 12(R9), D \ + MOVL D, 12(R9) \ + ADDL 16(R9), E \ + MOVL E, 16(R9) + + + +// Helper macros for PRECALC, which does precomputations +#define PRECALC_0(OFFSET) \ + VMOVDQU OFFSET(R10),X0 + +#define PRECALC_1(OFFSET) \ + VINSERTI128 $1, OFFSET(R13), Y0, Y0 + +#define PRECALC_2(YREG) \ + VPSHUFB Y10, Y0, YREG + +#define PRECALC_4(YREG,K_OFFSET) \ + VPADDD K_OFFSET(R8), YREG, Y0 + +#define PRECALC_7(OFFSET) \ + VMOVDQU Y0, (OFFSET*2)(R14) + + +// Message scheduling pre-compute for rounds 0-15 +// R13 is a pointer to even 64-byte block +// R10 is a pointer to odd 64-byte block +// R14 is a pointer to temp buffer +// X0 is used as temp register +// YREG is clobbered as part of computation +// OFFSET chooses 16 byte chunk within a block +// R8 is a pointer to constants block +// K_OFFSET chooses K constants relevant to this round +// X10 holds swap mask +#define PRECALC_00_15(OFFSET,YREG) \ + PRECALC_0(OFFSET) \ + PRECALC_1(OFFSET) \ + PRECALC_2(YREG) \ + PRECALC_4(YREG,0x0) \ + PRECALC_7(OFFSET) + + +// Helper macros for PRECALC_16_31 +#define PRECALC_16(REG_SUB_16,REG_SUB_12,REG_SUB_4,REG) \ + VPALIGNR $8, REG_SUB_16, REG_SUB_12, REG \ // w[i-14] + VPSRLDQ $4, REG_SUB_4, Y0 // w[i-3] + +#define PRECALC_17(REG_SUB_16,REG_SUB_8,REG) \ + VPXOR REG_SUB_8, REG, REG \ + VPXOR REG_SUB_16, Y0, Y0 + +#define PRECALC_18(REG) \ + VPXOR Y0, REG, REG \ + VPSLLDQ $12, REG, Y9 + +#define PRECALC_19(REG) \ + VPSLLD $1, REG, Y0 \ + VPSRLD $31, REG, REG + +#define PRECALC_20(REG) \ + VPOR REG, Y0, Y0 \ + VPSLLD $2, Y9, REG + +#define PRECALC_21(REG) \ + VPSRLD $30, Y9, Y9 \ + VPXOR REG, Y0, Y0 + +#define PRECALC_23(REG,K_OFFSET,OFFSET) \ + VPXOR Y9, Y0, REG \ + VPADDD K_OFFSET(R8), REG, Y0 \ + VMOVDQU Y0, (OFFSET)(R14) + +// Message scheduling pre-compute for rounds 16-31 +// calculating last 32 w[i] values in 8 XMM registers +// pre-calculate K+w[i] values and store to mem +// for later load by ALU add instruction. +// "brute force" vectorization for rounds 16-31 only +// due to w[i]->w[i-3] dependency. +// clobbers 5 input ymm registers REG_SUB* +// uses X0 and X9 as temp registers +// As always, R8 is a pointer to constants block +// and R14 is a pointer to temp buffer +#define PRECALC_16_31(REG,REG_SUB_4,REG_SUB_8,REG_SUB_12,REG_SUB_16,K_OFFSET,OFFSET) \ + PRECALC_16(REG_SUB_16,REG_SUB_12,REG_SUB_4,REG) \ + PRECALC_17(REG_SUB_16,REG_SUB_8,REG) \ + PRECALC_18(REG) \ + PRECALC_19(REG) \ + PRECALC_20(REG) \ + PRECALC_21(REG) \ + PRECALC_23(REG,K_OFFSET,OFFSET) + + +// Helper macros for PRECALC_32_79 +#define PRECALC_32(REG_SUB_8,REG_SUB_4) \ + VPALIGNR $8, REG_SUB_8, REG_SUB_4, Y0 + +#define PRECALC_33(REG_SUB_28,REG) \ + VPXOR REG_SUB_28, REG, REG + +#define PRECALC_34(REG_SUB_16) \ + VPXOR REG_SUB_16, Y0, Y0 + +#define PRECALC_35(REG) \ + VPXOR Y0, REG, REG + +#define PRECALC_36(REG) \ + VPSLLD $2, REG, Y0 + +#define PRECALC_37(REG) \ + VPSRLD $30, REG, REG \ + VPOR REG, Y0, REG + +#define PRECALC_39(REG,K_OFFSET,OFFSET) \ + VPADDD K_OFFSET(R8), REG, Y0 \ + VMOVDQU Y0, (OFFSET)(R14) + +// Message scheduling pre-compute for rounds 32-79 +// In SHA-1 specification we have: +// w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]) rol 1 +// Which is the same as: +// w[i] = (w[i-6] ^ w[i-16] ^ w[i-28] ^ w[i-32]) rol 2 +// This allows for more efficient vectorization, +// since w[i]->w[i-3] dependency is broken +#define PRECALC_32_79(REG,REG_SUB_4,REG_SUB_8,REG_SUB_16,REG_SUB_28,K_OFFSET,OFFSET) \ + PRECALC_32(REG_SUB_8,REG_SUB_4) \ + PRECALC_33(REG_SUB_28,REG) \ + PRECALC_34(REG_SUB_16) \ + PRECALC_35(REG) \ + PRECALC_36(REG) \ + PRECALC_37(REG) \ + PRECALC_39(REG,K_OFFSET,OFFSET) + +#define PRECALC \ + PRECALC_00_15(0,Y15) \ + PRECALC_00_15(0x10,Y14) \ + PRECALC_00_15(0x20,Y13) \ + PRECALC_00_15(0x30,Y12) \ + PRECALC_16_31(Y8,Y12,Y13,Y14,Y15,0,0x80) \ + PRECALC_16_31(Y7,Y8,Y12,Y13,Y14,0x20,0xa0) \ + PRECALC_16_31(Y5,Y7,Y8,Y12,Y13,0x20,0xc0) \ + PRECALC_16_31(Y3,Y5,Y7,Y8,Y12,0x20,0xe0) \ + PRECALC_32_79(Y15,Y3,Y5,Y8,Y14,0x20,0x100) \ + PRECALC_32_79(Y14,Y15,Y3,Y7,Y13,0x20,0x120) \ + PRECALC_32_79(Y13,Y14,Y15,Y5,Y12,0x40,0x140) \ + PRECALC_32_79(Y12,Y13,Y14,Y3,Y8,0x40,0x160) \ + PRECALC_32_79(Y8,Y12,Y13,Y15,Y7,0x40,0x180) \ + PRECALC_32_79(Y7,Y8,Y12,Y14,Y5,0x40,0x1a0) \ + PRECALC_32_79(Y5,Y7,Y8,Y13,Y3,0x40,0x1c0) \ + PRECALC_32_79(Y3,Y5,Y7,Y12,Y15,0x60,0x1e0) \ + PRECALC_32_79(Y15,Y3,Y5,Y8,Y14,0x60,0x200) \ + PRECALC_32_79(Y14,Y15,Y3,Y7,Y13,0x60,0x220) \ + PRECALC_32_79(Y13,Y14,Y15,Y5,Y12,0x60,0x240) \ + PRECALC_32_79(Y12,Y13,Y14,Y3,Y8,0x60,0x260) + +// Macros calculating individual rounds have general forn +// CALC_ROUND_PRE + PRECALC_ROUND + CALC_ROUND_POST +// CALC_ROUND_{PRE,POST} macros follow + +#define CALC_F1_PRE(OFFSET,REG_A,REG_B,REG_C,REG_E) \ + ADDL OFFSET(R15),REG_E \ + ANDNL REG_C,REG_A,BP \ + LEAL (REG_E)(REG_B*1), REG_E \ // Add F from the previous round + RORXL $0x1b, REG_A, R12 \ + RORXL $2, REG_A, REG_B // for next round + +// Calculate F for the next round +#define CALC_F1_POST(REG_A,REG_B,REG_E) \ + ANDL REG_B,REG_A \ // b&c + XORL BP, REG_A \ // F1 = (b&c) ^ (~b&d) + LEAL (REG_E)(R12*1), REG_E // E += A >>> 5 + + +// Registers are cycleickly rotated DX -> AX -> DI -> SI -> BX -> CX +#define CALC_0 \ + MOVL SI, BX \ // Precalculating first round + RORXL $2, SI, SI \ + ANDNL AX, BX, BP \ + ANDL DI, BX \ + XORL BP, BX \ + CALC_F1_PRE(0x0,CX,BX,DI,DX) \ + PRECALC_0(0x80) \ + CALC_F1_POST(CX,SI,DX) + +#define CALC_1 \ + CALC_F1_PRE(0x4,DX,CX,SI,AX) \ + PRECALC_1(0x80) \ + CALC_F1_POST(DX,BX,AX) + +#define CALC_2 \ + CALC_F1_PRE(0x8,AX,DX,BX,DI) \ + PRECALC_2(Y15) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_3 \ + CALC_F1_PRE(0xc,DI,AX,CX,SI) \ + CALC_F1_POST(DI,DX,SI) + +#define CALC_4 \ + CALC_F1_PRE(0x20,SI,DI,DX,BX) \ + PRECALC_4(Y15,0x0) \ + CALC_F1_POST(SI,AX,BX) + +#define CALC_5 \ + CALC_F1_PRE(0x24,BX,SI,AX,CX) \ + CALC_F1_POST(BX,DI,CX) + +#define CALC_6 \ + CALC_F1_PRE(0x28,CX,BX,DI,DX) \ + CALC_F1_POST(CX,SI,DX) + +#define CALC_7 \ + CALC_F1_PRE(0x2c,DX,CX,SI,AX) \ + PRECALC_7(0x0) \ + CALC_F1_POST(DX,BX,AX) + +#define CALC_8 \ + CALC_F1_PRE(0x40,AX,DX,BX,DI) \ + PRECALC_0(0x90) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_9 \ + CALC_F1_PRE(0x44,DI,AX,CX,SI) \ + PRECALC_1(0x90) \ + CALC_F1_POST(DI,DX,SI) + +#define CALC_10 \ + CALC_F1_PRE(0x48,SI,DI,DX,BX) \ + PRECALC_2(Y14) \ + CALC_F1_POST(SI,AX,BX) + +#define CALC_11 \ + CALC_F1_PRE(0x4c,BX,SI,AX,CX) \ + CALC_F1_POST(BX,DI,CX) + +#define CALC_12 \ + CALC_F1_PRE(0x60,CX,BX,DI,DX) \ + PRECALC_4(Y14,0x0) \ + CALC_F1_POST(CX,SI,DX) + +#define CALC_13 \ + CALC_F1_PRE(0x64,DX,CX,SI,AX) \ + CALC_F1_POST(DX,BX,AX) + +#define CALC_14 \ + CALC_F1_PRE(0x68,AX,DX,BX,DI) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_15 \ + CALC_F1_PRE(0x6c,DI,AX,CX,SI) \ + PRECALC_7(0x10) \ + CALC_F1_POST(DI,DX,SI) + +#define CALC_16 \ + CALC_F1_PRE(0x80,SI,DI,DX,BX) \ + PRECALC_0(0xa0) \ + CALC_F1_POST(SI,AX,BX) + +#define CALC_17 \ + CALC_F1_PRE(0x84,BX,SI,AX,CX) \ + PRECALC_1(0xa0) \ + CALC_F1_POST(BX,DI,CX) + +#define CALC_18 \ + CALC_F1_PRE(0x88,CX,BX,DI,DX) \ + PRECALC_2(Y13) \ + CALC_F1_POST(CX,SI,DX) + + +#define CALC_F2_PRE(OFFSET,REG_A,REG_B,REG_E) \ + ADDL OFFSET(R15),REG_E \ + LEAL (REG_E)(REG_B*1), REG_E \ // Add F from the previous round + RORXL $0x1b, REG_A, R12 \ + RORXL $2, REG_A, REG_B // for next round + +#define CALC_F2_POST(REG_A,REG_B,REG_C,REG_E) \ + XORL REG_B, REG_A \ + ADDL R12, REG_E \ + XORL REG_C, REG_A + +#define CALC_19 \ + CALC_F2_PRE(0x8c,DX,CX,AX) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_20 \ + CALC_F2_PRE(0xa0,AX,DX,DI) \ + PRECALC_4(Y13,0x0) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_21 \ + CALC_F2_PRE(0xa4,DI,AX,SI) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_22 \ + CALC_F2_PRE(0xa8,SI,DI,BX) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_23 \ + CALC_F2_PRE(0xac,BX,SI,CX) \ + PRECALC_7(0x20) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_24 \ + CALC_F2_PRE(0xc0,CX,BX,DX) \ + PRECALC_0(0xb0) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_25 \ + CALC_F2_PRE(0xc4,DX,CX,AX) \ + PRECALC_1(0xb0) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_26 \ + CALC_F2_PRE(0xc8,AX,DX,DI) \ + PRECALC_2(Y12) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_27 \ + CALC_F2_PRE(0xcc,DI,AX,SI) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_28 \ + CALC_F2_PRE(0xe0,SI,DI,BX) \ + PRECALC_4(Y12,0x0) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_29 \ + CALC_F2_PRE(0xe4,BX,SI,CX) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_30 \ + CALC_F2_PRE(0xe8,CX,BX,DX) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_31 \ + CALC_F2_PRE(0xec,DX,CX,AX) \ + PRECALC_7(0x30) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_32 \ + CALC_F2_PRE(0x100,AX,DX,DI) \ + PRECALC_16(Y15,Y14,Y12,Y8) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_33 \ + CALC_F2_PRE(0x104,DI,AX,SI) \ + PRECALC_17(Y15,Y13,Y8) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_34 \ + CALC_F2_PRE(0x108,SI,DI,BX) \ + PRECALC_18(Y8) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_35 \ + CALC_F2_PRE(0x10c,BX,SI,CX) \ + PRECALC_19(Y8) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_36 \ + CALC_F2_PRE(0x120,CX,BX,DX) \ + PRECALC_20(Y8) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_37 \ + CALC_F2_PRE(0x124,DX,CX,AX) \ + PRECALC_21(Y8) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_38 \ + CALC_F2_PRE(0x128,AX,DX,DI) \ + CALC_F2_POST(AX,CX,BX,DI) + + +#define CALC_F3_PRE(OFFSET,REG_E) \ + ADDL OFFSET(R15),REG_E + +#define CALC_F3_POST(REG_A,REG_B,REG_C,REG_E,REG_TB) \ + LEAL (REG_E)(REG_TB*1), REG_E \ // Add F from the previous round + MOVL REG_B, BP \ + ORL REG_A, BP \ + RORXL $0x1b, REG_A, R12 \ + RORXL $2, REG_A, REG_TB \ + ANDL REG_C, BP \ // Calculate F for the next round + ANDL REG_B, REG_A \ + ORL BP, REG_A \ + ADDL R12, REG_E + +#define CALC_39 \ + CALC_F3_PRE(0x12c,SI) \ + PRECALC_23(Y8,0x0,0x80) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_40 \ + CALC_F3_PRE(0x140,BX) \ + PRECALC_16(Y14,Y13,Y8,Y7) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_41 \ + CALC_F3_PRE(0x144,CX) \ + PRECALC_17(Y14,Y12,Y7) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_42 \ + CALC_F3_PRE(0x148,DX) \ + PRECALC_18(Y7) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_43 \ + CALC_F3_PRE(0x14c,AX) \ + PRECALC_19(Y7) \ + CALC_F3_POST(DX,BX,SI,AX,CX) + +#define CALC_44 \ + CALC_F3_PRE(0x160,DI) \ + PRECALC_20(Y7) \ + CALC_F3_POST(AX,CX,BX,DI,DX) + +#define CALC_45 \ + CALC_F3_PRE(0x164,SI) \ + PRECALC_21(Y7) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_46 \ + CALC_F3_PRE(0x168,BX) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_47 \ + CALC_F3_PRE(0x16c,CX) \ + VPXOR Y9, Y0, Y7 \ + VPADDD 0x20(R8), Y7, Y0 \ + VMOVDQU Y0, 0xa0(R14) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_48 \ + CALC_F3_PRE(0x180,DX) \ + PRECALC_16(Y13,Y12,Y7,Y5) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_49 \ + CALC_F3_PRE(0x184,AX) \ + PRECALC_17(Y13,Y8,Y5) \ + CALC_F3_POST(DX,BX,SI,AX,CX) + +#define CALC_50 \ + CALC_F3_PRE(0x188,DI) \ + PRECALC_18(Y5) \ + CALC_F3_POST(AX,CX,BX,DI,DX) + +#define CALC_51 \ + CALC_F3_PRE(0x18c,SI) \ + PRECALC_19(Y5) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_52 \ + CALC_F3_PRE(0x1a0,BX) \ + PRECALC_20(Y5) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_53 \ + CALC_F3_PRE(0x1a4,CX) \ + PRECALC_21(Y5) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_54 \ + CALC_F3_PRE(0x1a8,DX) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_55 \ + CALC_F3_PRE(0x1ac,AX) \ + PRECALC_23(Y5,0x20,0xc0) \ + CALC_F3_POST(DX,BX,SI,AX,CX) + +#define CALC_56 \ + CALC_F3_PRE(0x1c0,DI) \ + PRECALC_16(Y12,Y8,Y5,Y3) \ + CALC_F3_POST(AX,CX,BX,DI,DX) + +#define CALC_57 \ + CALC_F3_PRE(0x1c4,SI) \ + PRECALC_17(Y12,Y7,Y3) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_58 \ + CALC_F3_PRE(0x1c8,BX) \ + PRECALC_18(Y3) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_59 \ + CALC_F2_PRE(0x1cc,BX,SI,CX) \ + PRECALC_19(Y3) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_60 \ + CALC_F2_PRE(0x1e0,CX,BX,DX) \ + PRECALC_20(Y3) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_61 \ + CALC_F2_PRE(0x1e4,DX,CX,AX) \ + PRECALC_21(Y3) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_62 \ + CALC_F2_PRE(0x1e8,AX,DX,DI) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_63 \ + CALC_F2_PRE(0x1ec,DI,AX,SI) \ + PRECALC_23(Y3,0x20,0xe0) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_64 \ + CALC_F2_PRE(0x200,SI,DI,BX) \ + PRECALC_32(Y5,Y3) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_65 \ + CALC_F2_PRE(0x204,BX,SI,CX) \ + PRECALC_33(Y14,Y15) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_66 \ + CALC_F2_PRE(0x208,CX,BX,DX) \ + PRECALC_34(Y8) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_67 \ + CALC_F2_PRE(0x20c,DX,CX,AX) \ + PRECALC_35(Y15) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_68 \ + CALC_F2_PRE(0x220,AX,DX,DI) \ + PRECALC_36(Y15) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_69 \ + CALC_F2_PRE(0x224,DI,AX,SI) \ + PRECALC_37(Y15) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_70 \ + CALC_F2_PRE(0x228,SI,DI,BX) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_71 \ + CALC_F2_PRE(0x22c,BX,SI,CX) \ + PRECALC_39(Y15,0x20,0x100) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_72 \ + CALC_F2_PRE(0x240,CX,BX,DX) \ + PRECALC_32(Y3,Y15) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_73 \ + CALC_F2_PRE(0x244,DX,CX,AX) \ + PRECALC_33(Y13,Y14) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_74 \ + CALC_F2_PRE(0x248,AX,DX,DI) \ + PRECALC_34(Y7) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_75 \ + CALC_F2_PRE(0x24c,DI,AX,SI) \ + PRECALC_35(Y14) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_76 \ + CALC_F2_PRE(0x260,SI,DI,BX) \ + PRECALC_36(Y14) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_77 \ + CALC_F2_PRE(0x264,BX,SI,CX) \ + PRECALC_37(Y14) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_78 \ + CALC_F2_PRE(0x268,CX,BX,DX) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_79 \ + ADDL 0x26c(R15), AX \ + LEAL (AX)(CX*1), AX \ + RORXL $0x1b, DX, R12 \ + PRECALC_39(Y14,0x20,0x120) \ + ADDL R12, AX + +// Similar to CALC_0 +#define CALC_80 \ + MOVL CX, DX \ + RORXL $2, CX, CX \ + ANDNL SI, DX, BP \ + ANDL BX, DX \ + XORL BP, DX \ + CALC_F1_PRE(0x10,AX,DX,BX,DI) \ + PRECALC_32(Y15,Y14) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_81 \ + CALC_F1_PRE(0x14,DI,AX,CX,SI) \ + PRECALC_33(Y12,Y13) \ + CALC_F1_POST(DI,DX,SI) + +#define CALC_82 \ + CALC_F1_PRE(0x18,SI,DI,DX,BX) \ + PRECALC_34(Y5) \ + CALC_F1_POST(SI,AX,BX) + +#define CALC_83 \ + CALC_F1_PRE(0x1c,BX,SI,AX,CX) \ + PRECALC_35(Y13) \ + CALC_F1_POST(BX,DI,CX) + +#define CALC_84 \ + CALC_F1_PRE(0x30,CX,BX,DI,DX) \ + PRECALC_36(Y13) \ + CALC_F1_POST(CX,SI,DX) + +#define CALC_85 \ + CALC_F1_PRE(0x34,DX,CX,SI,AX) \ + PRECALC_37(Y13) \ + CALC_F1_POST(DX,BX,AX) + +#define CALC_86 \ + CALC_F1_PRE(0x38,AX,DX,BX,DI) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_87 \ + CALC_F1_PRE(0x3c,DI,AX,CX,SI) \ + PRECALC_39(Y13,0x40,0x140) \ + CALC_F1_POST(DI,DX,SI) + +#define CALC_88 \ + CALC_F1_PRE(0x50,SI,DI,DX,BX) \ + PRECALC_32(Y14,Y13) \ + CALC_F1_POST(SI,AX,BX) + +#define CALC_89 \ + CALC_F1_PRE(0x54,BX,SI,AX,CX) \ + PRECALC_33(Y8,Y12) \ + CALC_F1_POST(BX,DI,CX) + +#define CALC_90 \ + CALC_F1_PRE(0x58,CX,BX,DI,DX) \ + PRECALC_34(Y3) \ + CALC_F1_POST(CX,SI,DX) + +#define CALC_91 \ + CALC_F1_PRE(0x5c,DX,CX,SI,AX) \ + PRECALC_35(Y12) \ + CALC_F1_POST(DX,BX,AX) + +#define CALC_92 \ + CALC_F1_PRE(0x70,AX,DX,BX,DI) \ + PRECALC_36(Y12) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_93 \ + CALC_F1_PRE(0x74,DI,AX,CX,SI) \ + PRECALC_37(Y12) \ + CALC_F1_POST(DI,DX,SI) + +#define CALC_94 \ + CALC_F1_PRE(0x78,SI,DI,DX,BX) \ + CALC_F1_POST(SI,AX,BX) + +#define CALC_95 \ + CALC_F1_PRE(0x7c,BX,SI,AX,CX) \ + PRECALC_39(Y12,0x40,0x160) \ + CALC_F1_POST(BX,DI,CX) + +#define CALC_96 \ + CALC_F1_PRE(0x90,CX,BX,DI,DX) \ + PRECALC_32(Y13,Y12) \ + CALC_F1_POST(CX,SI,DX) + +#define CALC_97 \ + CALC_F1_PRE(0x94,DX,CX,SI,AX) \ + PRECALC_33(Y7,Y8) \ + CALC_F1_POST(DX,BX,AX) + +#define CALC_98 \ + CALC_F1_PRE(0x98,AX,DX,BX,DI) \ + PRECALC_34(Y15) \ + CALC_F1_POST(AX,CX,DI) + +#define CALC_99 \ + CALC_F2_PRE(0x9c,DI,AX,SI) \ + PRECALC_35(Y8) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_100 \ + CALC_F2_PRE(0xb0,SI,DI,BX) \ + PRECALC_36(Y8) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_101 \ + CALC_F2_PRE(0xb4,BX,SI,CX) \ + PRECALC_37(Y8) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_102 \ + CALC_F2_PRE(0xb8,CX,BX,DX) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_103 \ + CALC_F2_PRE(0xbc,DX,CX,AX) \ + PRECALC_39(Y8,0x40,0x180) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_104 \ + CALC_F2_PRE(0xd0,AX,DX,DI) \ + PRECALC_32(Y12,Y8) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_105 \ + CALC_F2_PRE(0xd4,DI,AX,SI) \ + PRECALC_33(Y5,Y7) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_106 \ + CALC_F2_PRE(0xd8,SI,DI,BX) \ + PRECALC_34(Y14) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_107 \ + CALC_F2_PRE(0xdc,BX,SI,CX) \ + PRECALC_35(Y7) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_108 \ + CALC_F2_PRE(0xf0,CX,BX,DX) \ + PRECALC_36(Y7) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_109 \ + CALC_F2_PRE(0xf4,DX,CX,AX) \ + PRECALC_37(Y7) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_110 \ + CALC_F2_PRE(0xf8,AX,DX,DI) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_111 \ + CALC_F2_PRE(0xfc,DI,AX,SI) \ + PRECALC_39(Y7,0x40,0x1a0) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_112 \ + CALC_F2_PRE(0x110,SI,DI,BX) \ + PRECALC_32(Y8,Y7) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_113 \ + CALC_F2_PRE(0x114,BX,SI,CX) \ + PRECALC_33(Y3,Y5) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_114 \ + CALC_F2_PRE(0x118,CX,BX,DX) \ + PRECALC_34(Y13) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_115 \ + CALC_F2_PRE(0x11c,DX,CX,AX) \ + PRECALC_35(Y5) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_116 \ + CALC_F2_PRE(0x130,AX,DX,DI) \ + PRECALC_36(Y5) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_117 \ + CALC_F2_PRE(0x134,DI,AX,SI) \ + PRECALC_37(Y5) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_118 \ + CALC_F2_PRE(0x138,SI,DI,BX) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_119 \ + CALC_F3_PRE(0x13c,CX) \ + PRECALC_39(Y5,0x40,0x1c0) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_120 \ + CALC_F3_PRE(0x150,DX) \ + PRECALC_32(Y7,Y5) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_121 \ + CALC_F3_PRE(0x154,AX) \ + PRECALC_33(Y15,Y3) \ + CALC_F3_POST(DX,BX,SI,AX,CX) + +#define CALC_122 \ + CALC_F3_PRE(0x158,DI) \ + PRECALC_34(Y12) \ + CALC_F3_POST(AX,CX,BX,DI,DX) + +#define CALC_123 \ + CALC_F3_PRE(0x15c,SI) \ + PRECALC_35(Y3) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_124 \ + CALC_F3_PRE(0x170,BX) \ + PRECALC_36(Y3) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_125 \ + CALC_F3_PRE(0x174,CX) \ + PRECALC_37(Y3) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_126 \ + CALC_F3_PRE(0x178,DX) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_127 \ + CALC_F3_PRE(0x17c,AX) \ + PRECALC_39(Y3,0x60,0x1e0) \ + CALC_F3_POST(DX,BX,SI,AX,CX) + +#define CALC_128 \ + CALC_F3_PRE(0x190,DI) \ + PRECALC_32(Y5,Y3) \ + CALC_F3_POST(AX,CX,BX,DI,DX) + +#define CALC_129 \ + CALC_F3_PRE(0x194,SI) \ + PRECALC_33(Y14,Y15) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_130 \ + CALC_F3_PRE(0x198,BX) \ + PRECALC_34(Y8) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_131 \ + CALC_F3_PRE(0x19c,CX) \ + PRECALC_35(Y15) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_132 \ + CALC_F3_PRE(0x1b0,DX) \ + PRECALC_36(Y15) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_133 \ + CALC_F3_PRE(0x1b4,AX) \ + PRECALC_37(Y15) \ + CALC_F3_POST(DX,BX,SI,AX,CX) + +#define CALC_134 \ + CALC_F3_PRE(0x1b8,DI) \ + CALC_F3_POST(AX,CX,BX,DI,DX) + +#define CALC_135 \ + CALC_F3_PRE(0x1bc,SI) \ + PRECALC_39(Y15,0x60,0x200) \ + CALC_F3_POST(DI,DX,CX,SI,AX) + +#define CALC_136 \ + CALC_F3_PRE(0x1d0,BX) \ + PRECALC_32(Y3,Y15) \ + CALC_F3_POST(SI,AX,DX,BX,DI) + +#define CALC_137 \ + CALC_F3_PRE(0x1d4,CX) \ + PRECALC_33(Y13,Y14) \ + CALC_F3_POST(BX,DI,AX,CX,SI) + +#define CALC_138 \ + CALC_F3_PRE(0x1d8,DX) \ + PRECALC_34(Y7) \ + CALC_F3_POST(CX,SI,DI,DX,BX) + +#define CALC_139 \ + CALC_F2_PRE(0x1dc,DX,CX,AX) \ + PRECALC_35(Y14) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_140 \ + CALC_F2_PRE(0x1f0,AX,DX,DI) \ + PRECALC_36(Y14) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_141 \ + CALC_F2_PRE(0x1f4,DI,AX,SI) \ + PRECALC_37(Y14) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_142 \ + CALC_F2_PRE(0x1f8,SI,DI,BX) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_143 \ + CALC_F2_PRE(0x1fc,BX,SI,CX) \ + PRECALC_39(Y14,0x60,0x220) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_144 \ + CALC_F2_PRE(0x210,CX,BX,DX) \ + PRECALC_32(Y15,Y14) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_145 \ + CALC_F2_PRE(0x214,DX,CX,AX) \ + PRECALC_33(Y12,Y13) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_146 \ + CALC_F2_PRE(0x218,AX,DX,DI) \ + PRECALC_34(Y5) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_147 \ + CALC_F2_PRE(0x21c,DI,AX,SI) \ + PRECALC_35(Y13) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_148 \ + CALC_F2_PRE(0x230,SI,DI,BX) \ + PRECALC_36(Y13) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_149 \ + CALC_F2_PRE(0x234,BX,SI,CX) \ + PRECALC_37(Y13) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_150 \ + CALC_F2_PRE(0x238,CX,BX,DX) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_151 \ + CALC_F2_PRE(0x23c,DX,CX,AX) \ + PRECALC_39(Y13,0x60,0x240) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_152 \ + CALC_F2_PRE(0x250,AX,DX,DI) \ + PRECALC_32(Y14,Y13) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_153 \ + CALC_F2_PRE(0x254,DI,AX,SI) \ + PRECALC_33(Y8,Y12) \ + CALC_F2_POST(DI,DX,CX,SI) + +#define CALC_154 \ + CALC_F2_PRE(0x258,SI,DI,BX) \ + PRECALC_34(Y3) \ + CALC_F2_POST(SI,AX,DX,BX) + +#define CALC_155 \ + CALC_F2_PRE(0x25c,BX,SI,CX) \ + PRECALC_35(Y12) \ + CALC_F2_POST(BX,DI,AX,CX) + +#define CALC_156 \ + CALC_F2_PRE(0x270,CX,BX,DX) \ + PRECALC_36(Y12) \ + CALC_F2_POST(CX,SI,DI,DX) + +#define CALC_157 \ + CALC_F2_PRE(0x274,DX,CX,AX) \ + PRECALC_37(Y12) \ + CALC_F2_POST(DX,BX,SI,AX) + +#define CALC_158 \ + CALC_F2_PRE(0x278,AX,DX,DI) \ + CALC_F2_POST(AX,CX,BX,DI) + +#define CALC_159 \ + ADDL 0x27c(R15),SI \ + LEAL (SI)(AX*1), SI \ + RORXL $0x1b, DI, R12 \ + PRECALC_39(Y12,0x60,0x260) \ + ADDL R12, SI + + + +#define CALC \ + MOVL (R9), CX \ + MOVL 4(R9), SI \ + MOVL 8(R9), DI \ + MOVL 12(R9), AX \ + MOVL 16(R9), DX \ + MOVQ SP, R14 \ + LEAQ (2*4*80+32)(SP), R15 \ + PRECALC \ // Precalc WK for first 2 blocks + XCHGQ R15, R14 \ +loop: \ // this loops is unrolled + CMPQ R10, R8 \ // we use R8 value (set below) as a signal of a last block + JNE begin \ + VZEROUPPER \ + RET \ +begin: \ + CALC_0 \ + CALC_1 \ + CALC_2 \ + CALC_3 \ + CALC_4 \ + CALC_5 \ + CALC_6 \ + CALC_7 \ + CALC_8 \ + CALC_9 \ + CALC_10 \ + CALC_11 \ + CALC_12 \ + CALC_13 \ + CALC_14 \ + CALC_15 \ + CALC_16 \ + CALC_17 \ + CALC_18 \ + CALC_19 \ + CALC_20 \ + CALC_21 \ + CALC_22 \ + CALC_23 \ + CALC_24 \ + CALC_25 \ + CALC_26 \ + CALC_27 \ + CALC_28 \ + CALC_29 \ + CALC_30 \ + CALC_31 \ + CALC_32 \ + CALC_33 \ + CALC_34 \ + CALC_35 \ + CALC_36 \ + CALC_37 \ + CALC_38 \ + CALC_39 \ + CALC_40 \ + CALC_41 \ + CALC_42 \ + CALC_43 \ + CALC_44 \ + CALC_45 \ + CALC_46 \ + CALC_47 \ + CALC_48 \ + CALC_49 \ + CALC_50 \ + CALC_51 \ + CALC_52 \ + CALC_53 \ + CALC_54 \ + CALC_55 \ + CALC_56 \ + CALC_57 \ + CALC_58 \ + CALC_59 \ + ADDQ $128, R10 \ // move to next even-64-byte block + CMPQ R10, R11 \ // is current block the last one? + CMOVQCC R8, R10 \ // signal the last iteration smartly + CALC_60 \ + CALC_61 \ + CALC_62 \ + CALC_63 \ + CALC_64 \ + CALC_65 \ + CALC_66 \ + CALC_67 \ + CALC_68 \ + CALC_69 \ + CALC_70 \ + CALC_71 \ + CALC_72 \ + CALC_73 \ + CALC_74 \ + CALC_75 \ + CALC_76 \ + CALC_77 \ + CALC_78 \ + CALC_79 \ + UPDATE_HASH(AX,DX,BX,SI,DI) \ + CMPQ R10, R8 \ // is current block the last one? + JE loop\ + MOVL DX, CX \ + CALC_80 \ + CALC_81 \ + CALC_82 \ + CALC_83 \ + CALC_84 \ + CALC_85 \ + CALC_86 \ + CALC_87 \ + CALC_88 \ + CALC_89 \ + CALC_90 \ + CALC_91 \ + CALC_92 \ + CALC_93 \ + CALC_94 \ + CALC_95 \ + CALC_96 \ + CALC_97 \ + CALC_98 \ + CALC_99 \ + CALC_100 \ + CALC_101 \ + CALC_102 \ + CALC_103 \ + CALC_104 \ + CALC_105 \ + CALC_106 \ + CALC_107 \ + CALC_108 \ + CALC_109 \ + CALC_110 \ + CALC_111 \ + CALC_112 \ + CALC_113 \ + CALC_114 \ + CALC_115 \ + CALC_116 \ + CALC_117 \ + CALC_118 \ + CALC_119 \ + CALC_120 \ + CALC_121 \ + CALC_122 \ + CALC_123 \ + CALC_124 \ + CALC_125 \ + CALC_126 \ + CALC_127 \ + CALC_128 \ + CALC_129 \ + CALC_130 \ + CALC_131 \ + CALC_132 \ + CALC_133 \ + CALC_134 \ + CALC_135 \ + CALC_136 \ + CALC_137 \ + CALC_138 \ + CALC_139 \ + ADDQ $128, R13 \ //move to next even-64-byte block + CMPQ R13, R11 \ //is current block the last one? + CMOVQCC R8, R10 \ + CALC_140 \ + CALC_141 \ + CALC_142 \ + CALC_143 \ + CALC_144 \ + CALC_145 \ + CALC_146 \ + CALC_147 \ + CALC_148 \ + CALC_149 \ + CALC_150 \ + CALC_151 \ + CALC_152 \ + CALC_153 \ + CALC_154 \ + CALC_155 \ + CALC_156 \ + CALC_157 \ + CALC_158 \ + CALC_159 \ + UPDATE_HASH(SI,DI,DX,CX,BX) \ + MOVL SI, R12 \ //Reset state for AVX2 reg permutation + MOVL DI, SI \ + MOVL DX, DI \ + MOVL BX, DX \ + MOVL CX, AX \ + MOVL R12, CX \ + XCHGQ R15, R14 \ + JMP loop + + + +TEXT ·blockAVX2(SB),$1408-32 + + MOVQ dig+0(FP), DI + MOVQ p_base+8(FP), SI + MOVQ p_len+16(FP), DX + SHRQ $6, DX + SHLQ $6, DX + + MOVQ $K_XMM_AR<>(SB), R8 + + MOVQ DI, R9 + MOVQ SI, R10 + LEAQ 64(SI), R13 + + ADDQ SI, DX + ADDQ $64, DX + MOVQ DX, R11 + + CMPQ R13, R11 + CMOVQCC R8, R13 + + MOVQ $BSWAP_SHUFB_CTL<>(SB), R8 + VMOVDQU (R8), Y10 + MOVQ $K_XMM_AR<>(SB), R8 //restore R8 + + CALC // RET is inside macros + + +// func checkAVX2() bool +// returns whether AVX2 is supported +TEXT ·checkAVX2(SB),NOSPLIT,$0 + CMPB runtime·support_avx2(SB), $1 + JE has + MOVB $0, ret+0(FP) + RET +has: + MOVB $1, ret+0(FP) + RET + + +DATA K_XMM_AR<>+0x00(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x04(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x08(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x0c(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x10(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x14(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x18(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x1c(SB)/4,$0x5a827999 +DATA K_XMM_AR<>+0x20(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x24(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x28(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x2c(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x30(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x34(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x38(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x3c(SB)/4,$0x6ed9eba1 +DATA K_XMM_AR<>+0x40(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x44(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x48(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x4c(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x50(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x54(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x58(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x5c(SB)/4,$0x8f1bbcdc +DATA K_XMM_AR<>+0x60(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x64(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x68(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x6c(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x70(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x74(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x78(SB)/4,$0xca62c1d6 +DATA K_XMM_AR<>+0x7c(SB)/4,$0xca62c1d6 +GLOBL K_XMM_AR<>(SB),RODATA,$128 + +DATA BSWAP_SHUFB_CTL<>+0x00(SB)/4,$0x00010203 +DATA BSWAP_SHUFB_CTL<>+0x04(SB)/4,$0x04050607 +DATA BSWAP_SHUFB_CTL<>+0x08(SB)/4,$0x08090a0b +DATA BSWAP_SHUFB_CTL<>+0x0c(SB)/4,$0x0c0d0e0f +DATA BSWAP_SHUFB_CTL<>+0x10(SB)/4,$0x00010203 +DATA BSWAP_SHUFB_CTL<>+0x14(SB)/4,$0x04050607 +DATA BSWAP_SHUFB_CTL<>+0x18(SB)/4,$0x08090a0b +DATA BSWAP_SHUFB_CTL<>+0x1c(SB)/4,$0x0c0d0e0f +GLOBL BSWAP_SHUFB_CTL<>(SB),RODATA,$32 diff --git a/src/crypto/sha1/sha1block_decl.go b/src/crypto/sha1/sha1block_decl.go index a85b74b8787174..6d2d073d137331 100644 --- a/src/crypto/sha1/sha1block_decl.go +++ b/src/crypto/sha1/sha1block_decl.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build amd64 amd64p32 arm 386 s390x +// +build amd64p32 arm 386 s390x package sha1 diff --git a/src/crypto/sha256/sha256block_amd64.s b/src/crypto/sha256/sha256block_amd64.s index c9f134c1069082..6ab3b52d65e163 100644 --- a/src/crypto/sha256/sha256block_amd64.s +++ b/src/crypto/sha256/sha256block_amd64.s @@ -1,4 +1,4 @@ -// Copyright 2013 The Go Authors. All rights reserved. +// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -9,7 +9,18 @@ // The algorithm is detailed in FIPS 180-4: // // http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf -// + +// The avx2-version is described in an Intel White-Paper: +// "Fast SHA-256 Implementations on Intel Architecture Processors" +// To find it, surf to http://www.intel.com/p/en_US/embedded +// and search for that title. +// AVX2 version by Intel, same algorithm as code in Linux kernel: +// https://github.com/torvalds/linux/blob/master/arch/x86/crypto/sha256-avx2-asm.S +// by +// James Guilford +// Kirk Yap +// Tim Chen + // Wt = Mt; for 0 <= t <= 15 // Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 // @@ -140,29 +151,439 @@ MSGSCHEDULE1(index); \ SHA256ROUND(index, const, a, b, c, d, e, f, g, h) -TEXT ·block(SB),0,$264-32 - MOVQ p_base+8(FP), SI - MOVQ p_len+16(FP), DX - SHRQ $6, DX - SHLQ $6, DX - - LEAQ (SI)(DX*1), DI - MOVQ DI, 256(SP) - CMPQ SI, DI - JEQ end - - MOVQ dig+0(FP), BP - MOVL (0*4)(BP), R8 // a = H0 - MOVL (1*4)(BP), R9 // b = H1 - MOVL (2*4)(BP), R10 // c = H2 - MOVL (3*4)(BP), R11 // d = H3 - MOVL (4*4)(BP), R12 // e = H4 - MOVL (5*4)(BP), R13 // f = H5 - MOVL (6*4)(BP), R14 // g = H6 - MOVL (7*4)(BP), R15 // h = H7 + +// Definitions for AVX2 version + +// addm (mem), reg +// Add reg to mem using reg-mem add and store +#define addm(P1, P2) \ + ADDL P2, P1; \ + MOVL P1, P2 + +#define XDWORD0 Y4 +#define XDWORD1 Y5 +#define XDWORD2 Y6 +#define XDWORD3 Y7 + +#define XWORD0 X4 +#define XWORD1 X5 +#define XWORD2 X6 +#define XWORD3 X7 + +#define XTMP0 Y0 +#define XTMP1 Y1 +#define XTMP2 Y2 +#define XTMP3 Y3 +#define XTMP4 Y8 +#define XTMP5 Y11 + +#define XFER Y9 + +#define BYTE_FLIP_MASK Y13 // mask to convert LE -> BE +#define X_BYTE_FLIP_MASK X13 + +#define NUM_BYTES DX +#define INP DI + +#define CTX SI // Beginning of digest in memory (a, b, c, ... , h) + +#define a AX +#define b BX +#define c CX +#define d R8 +#define e DX +#define f R9 +#define g R10 +#define h R11 + +#define old_h R11 + +#define TBL BP + +#define SRND SI // SRND is same register as CTX + +#define T1 R12 + +#define y0 R13 +#define y1 R14 +#define y2 R15 +#define y3 DI + +// Offsets +#define XFER_SIZE 2*64*4 +#define INP_END_SIZE 8 +#define INP_SIZE 8 +#define TMP_SIZE 4 + +#define _XFER 0 +#define _INP_END _XFER + XFER_SIZE +#define _INP _INP_END + INP_END_SIZE +#define _TMP _INP + INP_SIZE +#define STACK_SIZE _TMP + TMP_SIZE + +#define ROUND_AND_SCHED_N_0(disp, a, b, c, d, e, f, g, h, XDWORD0, XDWORD1, XDWORD2, XDWORD3) \ + ; \ // ############################# RND N + 0 ############################// + MOVL a, y3; \ // y3 = a // MAJA + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + ; \ + ADDL (disp + 0*4)(SP)(SRND*1), h; \ // h = k + w + h // disp = k + w + ORL c, y3; \ // y3 = a|c // MAJA + VPALIGNR $4, XDWORD2, XDWORD3, XTMP0; \ // XTMP0 = W[-7] + MOVL f, y2; \ // y2 = f // CH + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + XORL g, y2; \ // y2 = f^g // CH + VPADDD XDWORD0, XTMP0, XTMP0; \ // XTMP0 = W[-7] + W[-16] // y1 = (e >> 6) // S1 + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + ; \ + ANDL e, y2; \ // y2 = (f^g)&e // CH + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + ADDL h, d; \ // d = k + w + h + d // -- + ; \ + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + VPALIGNR $4, XDWORD0, XDWORD1, XTMP1; \ // XTMP1 = W[-15] + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + ; \ + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + VPSRLD $7, XTMP1, XTMP2; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL c, T1; \ // T1 = a&c // MAJB + ; \ + ADDL y0, y2; \ // y2 = S1 + CH // -- + VPSLLD $(32-7), XTMP1, XTMP3; \ + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ; \ + ADDL y2, d; \ // d = k + w + h + d + S1 + CH = d + t1 // -- + VPOR XTMP2, XTMP3, XTMP3; \ // XTMP3 = W[-15] ror 7 + ; \ + VPSRLD $18, XTMP1, XTMP2; \ + ADDL y2, h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + ADDL y3, h // h = t1 + S0 + MAJ // -- + +#define ROUND_AND_SCHED_N_1(disp, a, b, c, d, e, f, g, h, XDWORD0, XDWORD1, XDWORD2, XDWORD3) \ + ; \ // ################################### RND N + 1 ############################ + ; \ + MOVL a, y3; \ // y3 = a // MAJA + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + ADDL (disp + 1*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ORL c, y3; \ // y3 = a|c // MAJA + ; \ + VPSRLD $3, XTMP1, XTMP4; \ // XTMP4 = W[-15] >> 3 + MOVL f, y2; \ // y2 = f // CH + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + XORL g, y2; \ // y2 = f^g // CH + ; \ + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + ANDL e, y2; \ // y2 = (f^g)&e // CH + ADDL h, d; \ // d = k + w + h + d // -- + ; \ + VPSLLD $(32-18), XTMP1, XTMP1; \ + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + ; \ + VPXOR XTMP1, XTMP3, XTMP3; \ + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + ; \ + VPXOR XTMP2, XTMP3, XTMP3; \ // XTMP3 = W[-15] ror 7 ^ W[-15] ror 18 + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL c, T1; \ // T1 = a&c // MAJB + ADDL y0, y2; \ // y2 = S1 + CH // -- + ; \ + VPXOR XTMP4, XTMP3, XTMP1; \ // XTMP1 = s0 + VPSHUFD $-6, XDWORD3, XTMP2; \ // XTMP2 = W[-2] {BBAA} + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ; \ + VPADDD XTMP1, XTMP0, XTMP0; \ // XTMP0 = W[-16] + W[-7] + s0 + ADDL y2, d; \ // d = k + w + h + d + S1 + CH = d + t1 // -- + ADDL y2, h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + ADDL y3, h; \ // h = t1 + S0 + MAJ // -- + ; \ + VPSRLD $10, XTMP2, XTMP4 // XTMP4 = W[-2] >> 10 {BBAA} + +#define ROUND_AND_SCHED_N_2(disp, a, b, c, d, e, f, g, h, XDWORD0, XDWORD1, XDWORD2, XDWORD3) \ + ; \ // ################################### RND N + 2 ############################ + ; \ + MOVL a, y3; \ // y3 = a // MAJA + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + ADDL (disp + 2*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ; \ + VPSRLQ $19, XTMP2, XTMP3; \ // XTMP3 = W[-2] ror 19 {xBxA} + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + ORL c, y3; \ // y3 = a|c // MAJA + MOVL f, y2; \ // y2 = f // CH + XORL g, y2; \ // y2 = f^g // CH + ; \ + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + VPSRLQ $17, XTMP2, XTMP2; \ // XTMP2 = W[-2] ror 17 {xBxA} + ANDL e, y2; \ // y2 = (f^g)&e // CH + ; \ + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + VPXOR XTMP3, XTMP2, XTMP2; \ + ADDL h, d; \ // d = k + w + h + d // -- + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + VPXOR XTMP2, XTMP4, XTMP4; \ // XTMP4 = s1 {xBxA} + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + ; \ + MOVL f, _TMP(SP); \ + MOVQ $shuff_00BA<>(SB), f; \ // f is used to keep SHUF_00BA + VPSHUFB (f), XTMP4, XTMP4; \ // XTMP4 = s1 {00BA} + MOVL _TMP(SP), f; \ // f is restored + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + VPADDD XTMP4, XTMP0, XTMP0; \ // XTMP0 = {..., ..., W[1], W[0]} + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL c, T1; \ // T1 = a&c // MAJB + ADDL y0, y2; \ // y2 = S1 + CH // -- + VPSHUFD $80, XTMP0, XTMP2; \ // XTMP2 = W[-2] {DDCC} + ; \ + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ADDL y2, d; \ // d = k + w + h + d + S1 + CH = d + t1 // -- + ADDL y2, h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + ; \ + ADDL y3, h // h = t1 + S0 + MAJ // -- + +#define ROUND_AND_SCHED_N_3(disp, a, b, c, d, e, f, g, h, XDWORD0, XDWORD1, XDWORD2, XDWORD3) \ + ; \ // ################################### RND N + 3 ############################ + ; \ + MOVL a, y3; \ // y3 = a // MAJA + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + ADDL (disp + 3*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ORL c, y3; \ // y3 = a|c // MAJA + ; \ + VPSRLD $10, XTMP2, XTMP5; \ // XTMP5 = W[-2] >> 10 {DDCC} + MOVL f, y2; \ // y2 = f // CH + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + XORL g, y2; \ // y2 = f^g // CH + ; \ + VPSRLQ $19, XTMP2, XTMP3; \ // XTMP3 = W[-2] ror 19 {xDxC} + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + ANDL e, y2; \ // y2 = (f^g)&e // CH + ADDL h, d; \ // d = k + w + h + d // -- + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + ; \ + VPSRLQ $17, XTMP2, XTMP2; \ // XTMP2 = W[-2] ror 17 {xDxC} + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + ; \ + VPXOR XTMP3, XTMP2, XTMP2; \ + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + ADDL y0, y2; \ // y2 = S1 + CH // -- + ; \ + VPXOR XTMP2, XTMP5, XTMP5; \ // XTMP5 = s1 {xDxC} + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + ADDL y2, d; \ // d = k + w + h + d + S1 + CH = d + t1 // -- + ; \ + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + ; \ + MOVL f, _TMP(SP); \ // Save f + MOVQ $shuff_DC00<>(SB), f; \ // SHUF_00DC + VPSHUFB (f), XTMP5, XTMP5; \ // XTMP5 = s1 {DC00} + MOVL _TMP(SP), f; \ // Restore f + ; \ + VPADDD XTMP0, XTMP5, XDWORD0; \ // XDWORD0 = {W[3], W[2], W[1], W[0]} + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL c, T1; \ // T1 = a&c // MAJB + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ; \ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ADDL y2, h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + ADDL y3, h // h = t1 + S0 + MAJ // -- + +#define DO_ROUND_N_0(disp, a, b, c, d, e, f, g, h, old_h) \ + ; \ // ################################### RND N + 0 ########################### + MOVL f, y2; \ // y2 = f // CH + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + XORL g, y2; \ // y2 = f^g // CH + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + ANDL e, y2; \ // y2 = (f^g)&e // CH + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + MOVL a, y3; \ // y3 = a // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + ADDL (disp + 0*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ORL c, y3; \ // y3 = a|c // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + ANDL c, T1; \ // T1 = a&c // MAJB + ADDL y0, y2; \ // y2 = S1 + CH // -- + ; \ + ADDL h, d; \ // d = k + w + h + d // -- + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ADDL y2, d // d = k + w + h + d + S1 + CH = d + t1 // -- + +#define DO_ROUND_N_1(disp, a, b, c, d, e, f, g, h, old_h) \ + ; \ // ################################### RND N + 1 ########################### + ADDL y2, old_h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0 // -- + MOVL f, y2; \ // y2 = f // CH + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + XORL g, y2; \ // y2 = f^g // CH + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + ANDL e, y2; \ // y2 = (f^g)&e // CH + ADDL y3, old_h; \ // h = t1 + S0 + MAJ // -- + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + MOVL a, y3; \ // y3 = a // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + ADDL (disp + 1*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ORL c, y3; \ // y3 = a|c // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + ANDL c, T1; \ // T1 = a&c // MAJB + ADDL y0, y2; \ // y2 = S1 + CH // -- + ; \ + ADDL h, d; \ // d = k + w + h + d // -- + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ; \ + ADDL y2, d // d = k + w + h + d + S1 + CH = d + t1 // -- + +#define DO_ROUND_N_2(disp, a, b, c, d, e, f, g, h, old_h) \ + ; \ // ################################### RND N + 2 ############################## + ADDL y2, old_h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + MOVL f, y2; \ // y2 = f // CH + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + XORL g, y2; \ // y2 = f^g // CH + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + ANDL e, y2; \ // y2 = (f^g)&e // CH + ADDL y3, old_h; \ // h = t1 + S0 + MAJ // -- + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + MOVL a, y3; \ // y3 = a // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + ADDL (disp + 2*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ORL c, y3; \ // y3 = a|c // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + ANDL c, T1; \ // T1 = a&c // MAJB + ADDL y0, y2; \ // y2 = S1 + CH // -- + ; \ + ADDL h, d; \ // d = k + w + h + d // -- + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ; \ + ADDL y2, d // d = k + w + h + d + S1 + CH = d + t1 // -- + +#define DO_ROUND_N_3(disp, a, b, c, d, e, f, g, h, old_h) \ + ; \ // ################################### RND N + 3 ########################### + ADDL y2, old_h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + MOVL f, y2; \ // y2 = f // CH + RORXL $25, e, y0; \ // y0 = e >> 25 // S1A + RORXL $11, e, y1; \ // y1 = e >> 11 // S1B + XORL g, y2; \ // y2 = f^g // CH + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) // S1 + RORXL $6, e, y1; \ // y1 = (e >> 6) // S1 + ANDL e, y2; \ // y2 = (f^g)&e // CH + ADDL y3, old_h; \ // h = t1 + S0 + MAJ // -- + ; \ + XORL y1, y0; \ // y0 = (e>>25) ^ (e>>11) ^ (e>>6) // S1 + RORXL $13, a, T1; \ // T1 = a >> 13 // S0B + XORL g, y2; \ // y2 = CH = ((f^g)&e)^g // CH + RORXL $22, a, y1; \ // y1 = a >> 22 // S0A + MOVL a, y3; \ // y3 = a // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) // S0 + RORXL $2, a, T1; \ // T1 = (a >> 2) // S0 + ADDL (disp + 3*4)(SP)(SRND*1), h; \ // h = k + w + h // -- + ORL c, y3; \ // y3 = a|c // MAJA + ; \ + XORL T1, y1; \ // y1 = (a>>22) ^ (a>>13) ^ (a>>2) // S0 + MOVL a, T1; \ // T1 = a // MAJB + ANDL b, y3; \ // y3 = (a|c)&b // MAJA + ANDL c, T1; \ // T1 = a&c // MAJB + ADDL y0, y2; \ // y2 = S1 + CH // -- + ; \ + ADDL h, d; \ // d = k + w + h + d // -- + ORL T1, y3; \ // y3 = MAJ = (a|c)&b)|(a&c) // MAJ + ADDL y1, h; \ // h = k + w + h + S0 // -- + ; \ + ADDL y2, d; \ // d = k + w + h + d + S1 + CH = d + t1 // -- + ; \ + ADDL y2, h; \ // h = k + w + h + S0 + S1 + CH = t1 + S0// -- + ; \ + ADDL y3, h // h = t1 + S0 + MAJ // -- + +TEXT ·block(SB), 0, $536-32 + CMPB runtime·support_avx2(SB), $1 + JE avx2 + + MOVQ p_base+8(FP), SI + MOVQ p_len+16(FP), DX + SHRQ $6, DX + SHLQ $6, DX + + LEAQ (SI)(DX*1), DI + MOVQ DI, 256(SP) + CMPQ SI, DI + JEQ end + + MOVQ dig+0(FP), BP + MOVL (0*4)(BP), R8 // a = H0 + MOVL (1*4)(BP), R9 // b = H1 + MOVL (2*4)(BP), R10 // c = H2 + MOVL (3*4)(BP), R11 // d = H3 + MOVL (4*4)(BP), R12 // e = H4 + MOVL (5*4)(BP), R13 // f = H5 + MOVL (6*4)(BP), R14 // g = H6 + MOVL (7*4)(BP), R15 // h = H7 loop: - MOVQ SP, BP // message schedule + MOVQ SP, BP SHA256ROUND0(0, 0x428a2f98, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND0(1, 0x71374491, R15, R8, R9, R10, R11, R12, R13, R14) @@ -230,27 +651,391 @@ loop: SHA256ROUND1(62, 0xbef9a3f7, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND1(63, 0xc67178f2, R9, R10, R11, R12, R13, R14, R15, R8) - MOVQ dig+0(FP), BP - ADDL (0*4)(BP), R8 // H0 = a + H0 - MOVL R8, (0*4)(BP) - ADDL (1*4)(BP), R9 // H1 = b + H1 - MOVL R9, (1*4)(BP) - ADDL (2*4)(BP), R10 // H2 = c + H2 - MOVL R10, (2*4)(BP) - ADDL (3*4)(BP), R11 // H3 = d + H3 - MOVL R11, (3*4)(BP) - ADDL (4*4)(BP), R12 // H4 = e + H4 - MOVL R12, (4*4)(BP) - ADDL (5*4)(BP), R13 // H5 = f + H5 - MOVL R13, (5*4)(BP) - ADDL (6*4)(BP), R14 // H6 = g + H6 - MOVL R14, (6*4)(BP) - ADDL (7*4)(BP), R15 // H7 = h + H7 - MOVL R15, (7*4)(BP) - - ADDQ $64, SI - CMPQ SI, 256(SP) - JB loop + MOVQ dig+0(FP), BP + ADDL (0*4)(BP), R8 // H0 = a + H0 + MOVL R8, (0*4)(BP) + ADDL (1*4)(BP), R9 // H1 = b + H1 + MOVL R9, (1*4)(BP) + ADDL (2*4)(BP), R10 // H2 = c + H2 + MOVL R10, (2*4)(BP) + ADDL (3*4)(BP), R11 // H3 = d + H3 + MOVL R11, (3*4)(BP) + ADDL (4*4)(BP), R12 // H4 = e + H4 + MOVL R12, (4*4)(BP) + ADDL (5*4)(BP), R13 // H5 = f + H5 + MOVL R13, (5*4)(BP) + ADDL (6*4)(BP), R14 // H6 = g + H6 + MOVL R14, (6*4)(BP) + ADDL (7*4)(BP), R15 // H7 = h + H7 + MOVL R15, (7*4)(BP) + + ADDQ $64, SI + CMPQ SI, 256(SP) + JB loop end: RET + +avx2: + MOVQ dig+0(FP), CTX // d.h[8] + MOVQ p_base+8(FP), INP + MOVQ p_len+16(FP), NUM_BYTES + + LEAQ -64(INP)(NUM_BYTES*1), NUM_BYTES // Pointer to the last block + MOVQ NUM_BYTES, _INP_END(SP) + + CMPQ NUM_BYTES, INP + JE avx2_only_one_block + + // Load initial digest + MOVL 0(CTX), a // a = H0 + MOVL 4(CTX), b // b = H1 + MOVL 8(CTX), c // c = H2 + MOVL 12(CTX), d // d = H3 + MOVL 16(CTX), e // e = H4 + MOVL 20(CTX), f // f = H5 + MOVL 24(CTX), g // g = H6 + MOVL 28(CTX), h // h = H7 + +avx2_loop0: // at each iteration works with one block (512 bit) + + VMOVDQU (0*32)(INP), XTMP0 + VMOVDQU (1*32)(INP), XTMP1 + VMOVDQU (2*32)(INP), XTMP2 + VMOVDQU (3*32)(INP), XTMP3 + + MOVQ $flip_mask<>(SB), BP // BYTE_FLIP_MASK + VMOVDQU (BP), BYTE_FLIP_MASK + + // Apply Byte Flip Mask: LE -> BE + VPSHUFB BYTE_FLIP_MASK, XTMP0, XTMP0 + VPSHUFB BYTE_FLIP_MASK, XTMP1, XTMP1 + VPSHUFB BYTE_FLIP_MASK, XTMP2, XTMP2 + VPSHUFB BYTE_FLIP_MASK, XTMP3, XTMP3 + + // Transpose data into high/low parts + VPERM2I128 $0x20, XTMP2, XTMP0, XDWORD0 // w3, w2, w1, w0 + VPERM2I128 $0x31, XTMP2, XTMP0, XDWORD1 // w7, w6, w5, w4 + VPERM2I128 $0x20, XTMP3, XTMP1, XDWORD2 // w11, w10, w9, w8 + VPERM2I128 $0x31, XTMP3, XTMP1, XDWORD3 // w15, w14, w13, w12 + + MOVQ $K256<>(SB), TBL // Loading address of table with round-specific constants + +avx2_last_block_enter: + ADDQ $64, INP + MOVQ INP, _INP(SP) + XORQ SRND, SRND + +avx2_loop1: // for w0 - w47 + // Do 4 rounds and scheduling + VPADDD 0*32(TBL)(SRND*1), XDWORD0, XFER + VMOVDQU XFER, (_XFER + 0*32)(SP)(SRND*1) + ROUND_AND_SCHED_N_0(_XFER + 0*32, a, b, c, d, e, f, g, h, XDWORD0, XDWORD1, XDWORD2, XDWORD3) + ROUND_AND_SCHED_N_1(_XFER + 0*32, h, a, b, c, d, e, f, g, XDWORD0, XDWORD1, XDWORD2, XDWORD3) + ROUND_AND_SCHED_N_2(_XFER + 0*32, g, h, a, b, c, d, e, f, XDWORD0, XDWORD1, XDWORD2, XDWORD3) + ROUND_AND_SCHED_N_3(_XFER + 0*32, f, g, h, a, b, c, d, e, XDWORD0, XDWORD1, XDWORD2, XDWORD3) + + // Do 4 rounds and scheduling + VPADDD 1*32(TBL)(SRND*1), XDWORD1, XFER + VMOVDQU XFER, (_XFER + 1*32)(SP)(SRND*1) + ROUND_AND_SCHED_N_0(_XFER + 1*32, e, f, g, h, a, b, c, d, XDWORD1, XDWORD2, XDWORD3, XDWORD0) + ROUND_AND_SCHED_N_1(_XFER + 1*32, d, e, f, g, h, a, b, c, XDWORD1, XDWORD2, XDWORD3, XDWORD0) + ROUND_AND_SCHED_N_2(_XFER + 1*32, c, d, e, f, g, h, a, b, XDWORD1, XDWORD2, XDWORD3, XDWORD0) + ROUND_AND_SCHED_N_3(_XFER + 1*32, b, c, d, e, f, g, h, a, XDWORD1, XDWORD2, XDWORD3, XDWORD0) + + // Do 4 rounds and scheduling + VPADDD 2*32(TBL)(SRND*1), XDWORD2, XFER + VMOVDQU XFER, (_XFER + 2*32)(SP)(SRND*1) + ROUND_AND_SCHED_N_0(_XFER + 2*32, a, b, c, d, e, f, g, h, XDWORD2, XDWORD3, XDWORD0, XDWORD1) + ROUND_AND_SCHED_N_1(_XFER + 2*32, h, a, b, c, d, e, f, g, XDWORD2, XDWORD3, XDWORD0, XDWORD1) + ROUND_AND_SCHED_N_2(_XFER + 2*32, g, h, a, b, c, d, e, f, XDWORD2, XDWORD3, XDWORD0, XDWORD1) + ROUND_AND_SCHED_N_3(_XFER + 2*32, f, g, h, a, b, c, d, e, XDWORD2, XDWORD3, XDWORD0, XDWORD1) + + // Do 4 rounds and scheduling + VPADDD 3*32(TBL)(SRND*1), XDWORD3, XFER + VMOVDQU XFER, (_XFER + 3*32)(SP)(SRND*1) + ROUND_AND_SCHED_N_0(_XFER + 3*32, e, f, g, h, a, b, c, d, XDWORD3, XDWORD0, XDWORD1, XDWORD2) + ROUND_AND_SCHED_N_1(_XFER + 3*32, d, e, f, g, h, a, b, c, XDWORD3, XDWORD0, XDWORD1, XDWORD2) + ROUND_AND_SCHED_N_2(_XFER + 3*32, c, d, e, f, g, h, a, b, XDWORD3, XDWORD0, XDWORD1, XDWORD2) + ROUND_AND_SCHED_N_3(_XFER + 3*32, b, c, d, e, f, g, h, a, XDWORD3, XDWORD0, XDWORD1, XDWORD2) + + ADDQ $4*32, SRND + CMPQ SRND, $3*4*32 + JB avx2_loop1 + +avx2_loop2: + // w48 - w63 processed with no scheduliung (last 16 rounds) + VPADDD 0*32(TBL)(SRND*1), XDWORD0, XFER + VMOVDQU XFER, (_XFER + 0*32)(SP)(SRND*1) + DO_ROUND_N_0(_XFER + 0*32, a, b, c, d, e, f, g, h, h) + DO_ROUND_N_1(_XFER + 0*32, h, a, b, c, d, e, f, g, h) + DO_ROUND_N_2(_XFER + 0*32, g, h, a, b, c, d, e, f, g) + DO_ROUND_N_3(_XFER + 0*32, f, g, h, a, b, c, d, e, f) + + VPADDD 1*32(TBL)(SRND*1), XDWORD1, XFER + VMOVDQU XFER, (_XFER + 1*32)(SP)(SRND*1) + DO_ROUND_N_0(_XFER + 1*32, e, f, g, h, a, b, c, d, e) + DO_ROUND_N_1(_XFER + 1*32, d, e, f, g, h, a, b, c, d) + DO_ROUND_N_2(_XFER + 1*32, c, d, e, f, g, h, a, b, c) + DO_ROUND_N_3(_XFER + 1*32, b, c, d, e, f, g, h, a, b) + + ADDQ $2*32, SRND + + VMOVDQU XDWORD2, XDWORD0 + VMOVDQU XDWORD3, XDWORD1 + + CMPQ SRND, $4*4*32 + JB avx2_loop2 + + MOVQ dig+0(FP), CTX // d.h[8] + MOVQ _INP(SP), INP + + addm( 0(CTX), a) + addm( 4(CTX), b) + addm( 8(CTX), c) + addm( 12(CTX), d) + addm( 16(CTX), e) + addm( 20(CTX), f) + addm( 24(CTX), g) + addm( 28(CTX), h) + + CMPQ _INP_END(SP), INP + JB done_hash + + XORQ SRND, SRND + +avx2_loop3: // Do second block using previously scheduled results + DO_ROUND_N_0(_XFER + 0*32 + 16, a, b, c, d, e, f, g, h, a) + DO_ROUND_N_1(_XFER + 0*32 + 16, h, a, b, c, d, e, f, g, h) + DO_ROUND_N_2(_XFER + 0*32 + 16, g, h, a, b, c, d, e, f, g) + DO_ROUND_N_3(_XFER + 0*32 + 16, f, g, h, a, b, c, d, e, f) + + DO_ROUND_N_0(_XFER + 1*32 + 16, e, f, g, h, a, b, c, d, e) + DO_ROUND_N_1(_XFER + 1*32 + 16, d, e, f, g, h, a, b, c, d) + DO_ROUND_N_2(_XFER + 1*32 + 16, c, d, e, f, g, h, a, b, c) + DO_ROUND_N_3(_XFER + 1*32 + 16, b, c, d, e, f, g, h, a, b) + + ADDQ $2*32, SRND + CMPQ SRND, $4*4*32 + JB avx2_loop3 + + MOVQ dig+0(FP), CTX // d.h[8] + MOVQ _INP(SP), INP + ADDQ $64, INP + + addm( 0(CTX), a) + addm( 4(CTX), b) + addm( 8(CTX), c) + addm( 12(CTX), d) + addm( 16(CTX), e) + addm( 20(CTX), f) + addm( 24(CTX), g) + addm( 28(CTX), h) + + CMPQ _INP_END(SP), INP + JA avx2_loop0 + JB done_hash + +avx2_do_last_block: + + VMOVDQU 0(INP), XWORD0 + VMOVDQU 16(INP), XWORD1 + VMOVDQU 32(INP), XWORD2 + VMOVDQU 48(INP), XWORD3 + + MOVQ $flip_mask<>(SB), BP + VMOVDQU (BP), X_BYTE_FLIP_MASK + + VPSHUFB X_BYTE_FLIP_MASK, XWORD0, XWORD0 + VPSHUFB X_BYTE_FLIP_MASK, XWORD1, XWORD1 + VPSHUFB X_BYTE_FLIP_MASK, XWORD2, XWORD2 + VPSHUFB X_BYTE_FLIP_MASK, XWORD3, XWORD3 + + MOVQ $K256<>(SB), TBL + + JMP avx2_last_block_enter + +avx2_only_one_block: + // Load initial digest + MOVL 0(CTX), a // a = H0 + MOVL 4(CTX), b // b = H1 + MOVL 8(CTX), c // c = H2 + MOVL 12(CTX), d // d = H3 + MOVL 16(CTX), e // e = H4 + MOVL 20(CTX), f // f = H5 + MOVL 24(CTX), g // g = H6 + MOVL 28(CTX), h // h = H7 + + JMP avx2_do_last_block + +done_hash: + VZEROUPPER + RET + +// shuffle byte order from LE to BE +DATA flip_mask<>+0x00(SB)/8, $0x0405060700010203 +DATA flip_mask<>+0x08(SB)/8, $0x0c0d0e0f08090a0b +DATA flip_mask<>+0x10(SB)/8, $0x0405060700010203 +DATA flip_mask<>+0x18(SB)/8, $0x0c0d0e0f08090a0b +GLOBL flip_mask<>(SB), 8, $32 + +// shuffle xBxA -> 00BA +DATA shuff_00BA<>+0x00(SB)/8, $0x0b0a090803020100 +DATA shuff_00BA<>+0x08(SB)/8, $0xFFFFFFFFFFFFFFFF +DATA shuff_00BA<>+0x10(SB)/8, $0x0b0a090803020100 +DATA shuff_00BA<>+0x18(SB)/8, $0xFFFFFFFFFFFFFFFF +GLOBL shuff_00BA<>(SB), 8, $32 + +// shuffle xDxC -> DC00 +DATA shuff_DC00<>+0x00(SB)/8, $0xFFFFFFFFFFFFFFFF +DATA shuff_DC00<>+0x08(SB)/8, $0x0b0a090803020100 +DATA shuff_DC00<>+0x10(SB)/8, $0xFFFFFFFFFFFFFFFF +DATA shuff_DC00<>+0x18(SB)/8, $0x0b0a090803020100 +GLOBL shuff_DC00<>(SB), 8, $32 + +// Round specific constants +DATA K256<>+0x00(SB)/4, $0x428a2f98 // k1 +DATA K256<>+0x04(SB)/4, $0x71374491 // k2 +DATA K256<>+0x08(SB)/4, $0xb5c0fbcf // k3 +DATA K256<>+0x0c(SB)/4, $0xe9b5dba5 // k4 +DATA K256<>+0x10(SB)/4, $0x428a2f98 // k1 +DATA K256<>+0x14(SB)/4, $0x71374491 // k2 +DATA K256<>+0x18(SB)/4, $0xb5c0fbcf // k3 +DATA K256<>+0x1c(SB)/4, $0xe9b5dba5 // k4 + +DATA K256<>+0x20(SB)/4, $0x3956c25b // k5 - k8 +DATA K256<>+0x24(SB)/4, $0x59f111f1 +DATA K256<>+0x28(SB)/4, $0x923f82a4 +DATA K256<>+0x2c(SB)/4, $0xab1c5ed5 +DATA K256<>+0x30(SB)/4, $0x3956c25b +DATA K256<>+0x34(SB)/4, $0x59f111f1 +DATA K256<>+0x38(SB)/4, $0x923f82a4 +DATA K256<>+0x3c(SB)/4, $0xab1c5ed5 + +DATA K256<>+0x40(SB)/4, $0xd807aa98 // k9 - k12 +DATA K256<>+0x44(SB)/4, $0x12835b01 +DATA K256<>+0x48(SB)/4, $0x243185be +DATA K256<>+0x4c(SB)/4, $0x550c7dc3 +DATA K256<>+0x50(SB)/4, $0xd807aa98 +DATA K256<>+0x54(SB)/4, $0x12835b01 +DATA K256<>+0x58(SB)/4, $0x243185be +DATA K256<>+0x5c(SB)/4, $0x550c7dc3 + +DATA K256<>+0x60(SB)/4, $0x72be5d74 // k13 - k16 +DATA K256<>+0x64(SB)/4, $0x80deb1fe +DATA K256<>+0x68(SB)/4, $0x9bdc06a7 +DATA K256<>+0x6c(SB)/4, $0xc19bf174 +DATA K256<>+0x70(SB)/4, $0x72be5d74 +DATA K256<>+0x74(SB)/4, $0x80deb1fe +DATA K256<>+0x78(SB)/4, $0x9bdc06a7 +DATA K256<>+0x7c(SB)/4, $0xc19bf174 + +DATA K256<>+0x80(SB)/4, $0xe49b69c1 // k17 - k20 +DATA K256<>+0x84(SB)/4, $0xefbe4786 +DATA K256<>+0x88(SB)/4, $0x0fc19dc6 +DATA K256<>+0x8c(SB)/4, $0x240ca1cc +DATA K256<>+0x90(SB)/4, $0xe49b69c1 +DATA K256<>+0x94(SB)/4, $0xefbe4786 +DATA K256<>+0x98(SB)/4, $0x0fc19dc6 +DATA K256<>+0x9c(SB)/4, $0x240ca1cc + +DATA K256<>+0xa0(SB)/4, $0x2de92c6f // k21 - k24 +DATA K256<>+0xa4(SB)/4, $0x4a7484aa +DATA K256<>+0xa8(SB)/4, $0x5cb0a9dc +DATA K256<>+0xac(SB)/4, $0x76f988da +DATA K256<>+0xb0(SB)/4, $0x2de92c6f +DATA K256<>+0xb4(SB)/4, $0x4a7484aa +DATA K256<>+0xb8(SB)/4, $0x5cb0a9dc +DATA K256<>+0xbc(SB)/4, $0x76f988da + +DATA K256<>+0xc0(SB)/4, $0x983e5152 // k25 - k28 +DATA K256<>+0xc4(SB)/4, $0xa831c66d +DATA K256<>+0xc8(SB)/4, $0xb00327c8 +DATA K256<>+0xcc(SB)/4, $0xbf597fc7 +DATA K256<>+0xd0(SB)/4, $0x983e5152 +DATA K256<>+0xd4(SB)/4, $0xa831c66d +DATA K256<>+0xd8(SB)/4, $0xb00327c8 +DATA K256<>+0xdc(SB)/4, $0xbf597fc7 + +DATA K256<>+0xe0(SB)/4, $0xc6e00bf3 // k29 - k32 +DATA K256<>+0xe4(SB)/4, $0xd5a79147 +DATA K256<>+0xe8(SB)/4, $0x06ca6351 +DATA K256<>+0xec(SB)/4, $0x14292967 +DATA K256<>+0xf0(SB)/4, $0xc6e00bf3 +DATA K256<>+0xf4(SB)/4, $0xd5a79147 +DATA K256<>+0xf8(SB)/4, $0x06ca6351 +DATA K256<>+0xfc(SB)/4, $0x14292967 + +DATA K256<>+0x100(SB)/4, $0x27b70a85 +DATA K256<>+0x104(SB)/4, $0x2e1b2138 +DATA K256<>+0x108(SB)/4, $0x4d2c6dfc +DATA K256<>+0x10c(SB)/4, $0x53380d13 +DATA K256<>+0x110(SB)/4, $0x27b70a85 +DATA K256<>+0x114(SB)/4, $0x2e1b2138 +DATA K256<>+0x118(SB)/4, $0x4d2c6dfc +DATA K256<>+0x11c(SB)/4, $0x53380d13 + +DATA K256<>+0x120(SB)/4, $0x650a7354 +DATA K256<>+0x124(SB)/4, $0x766a0abb +DATA K256<>+0x128(SB)/4, $0x81c2c92e +DATA K256<>+0x12c(SB)/4, $0x92722c85 +DATA K256<>+0x130(SB)/4, $0x650a7354 +DATA K256<>+0x134(SB)/4, $0x766a0abb +DATA K256<>+0x138(SB)/4, $0x81c2c92e +DATA K256<>+0x13c(SB)/4, $0x92722c85 + +DATA K256<>+0x140(SB)/4, $0xa2bfe8a1 +DATA K256<>+0x144(SB)/4, $0xa81a664b +DATA K256<>+0x148(SB)/4, $0xc24b8b70 +DATA K256<>+0x14c(SB)/4, $0xc76c51a3 +DATA K256<>+0x150(SB)/4, $0xa2bfe8a1 +DATA K256<>+0x154(SB)/4, $0xa81a664b +DATA K256<>+0x158(SB)/4, $0xc24b8b70 +DATA K256<>+0x15c(SB)/4, $0xc76c51a3 + +DATA K256<>+0x160(SB)/4, $0xd192e819 +DATA K256<>+0x164(SB)/4, $0xd6990624 +DATA K256<>+0x168(SB)/4, $0xf40e3585 +DATA K256<>+0x16c(SB)/4, $0x106aa070 +DATA K256<>+0x170(SB)/4, $0xd192e819 +DATA K256<>+0x174(SB)/4, $0xd6990624 +DATA K256<>+0x178(SB)/4, $0xf40e3585 +DATA K256<>+0x17c(SB)/4, $0x106aa070 + +DATA K256<>+0x180(SB)/4, $0x19a4c116 +DATA K256<>+0x184(SB)/4, $0x1e376c08 +DATA K256<>+0x188(SB)/4, $0x2748774c +DATA K256<>+0x18c(SB)/4, $0x34b0bcb5 +DATA K256<>+0x190(SB)/4, $0x19a4c116 +DATA K256<>+0x194(SB)/4, $0x1e376c08 +DATA K256<>+0x198(SB)/4, $0x2748774c +DATA K256<>+0x19c(SB)/4, $0x34b0bcb5 + +DATA K256<>+0x1a0(SB)/4, $0x391c0cb3 +DATA K256<>+0x1a4(SB)/4, $0x4ed8aa4a +DATA K256<>+0x1a8(SB)/4, $0x5b9cca4f +DATA K256<>+0x1ac(SB)/4, $0x682e6ff3 +DATA K256<>+0x1b0(SB)/4, $0x391c0cb3 +DATA K256<>+0x1b4(SB)/4, $0x4ed8aa4a +DATA K256<>+0x1b8(SB)/4, $0x5b9cca4f +DATA K256<>+0x1bc(SB)/4, $0x682e6ff3 + +DATA K256<>+0x1c0(SB)/4, $0x748f82ee +DATA K256<>+0x1c4(SB)/4, $0x78a5636f +DATA K256<>+0x1c8(SB)/4, $0x84c87814 +DATA K256<>+0x1cc(SB)/4, $0x8cc70208 +DATA K256<>+0x1d0(SB)/4, $0x748f82ee +DATA K256<>+0x1d4(SB)/4, $0x78a5636f +DATA K256<>+0x1d8(SB)/4, $0x84c87814 +DATA K256<>+0x1dc(SB)/4, $0x8cc70208 + +DATA K256<>+0x1e0(SB)/4, $0x90befffa +DATA K256<>+0x1e4(SB)/4, $0xa4506ceb +DATA K256<>+0x1e8(SB)/4, $0xbef9a3f7 +DATA K256<>+0x1ec(SB)/4, $0xc67178f2 +DATA K256<>+0x1f0(SB)/4, $0x90befffa +DATA K256<>+0x1f4(SB)/4, $0xa4506ceb +DATA K256<>+0x1f8(SB)/4, $0xbef9a3f7 +DATA K256<>+0x1fc(SB)/4, $0xc67178f2 + +GLOBL K256<>(SB), (NOPTR + RODATA), $512 diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index c4f8b0816b0b00..40c17440d652dc 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -76,9 +76,10 @@ type Conn struct { input *block // application data waiting to be read hand bytes.Buffer // handshake data waiting to be read - // bytesSent counts the number of bytes of application data that have - // been sent. - bytesSent int64 + // bytesSent counts the bytes of application data sent. + // packetsSent counts packets. + bytesSent int64 + packetsSent int64 // activeCall is an atomic int32; the low bit is whether Close has // been called. the rest of the bits are the number of goroutines @@ -732,7 +733,7 @@ const ( // recordSizeBoostThreshold is the number of bytes of application data // sent after which the TLS record size will be increased to the // maximum. - recordSizeBoostThreshold = 1 * 1024 * 1024 + recordSizeBoostThreshold = 128 * 1024 ) // maxPayloadSizeForWrite returns the maximum TLS payload size to use for the @@ -788,7 +789,18 @@ func (c *Conn) maxPayloadSizeForWrite(typ recordType, explicitIVLen int) int { } } - return payloadBytes + // Allow packet growth in arithmetic progression up to max. + pkt := c.packetsSent + c.packetsSent++ + if pkt > 1000 { + return maxPlaintext // avoid overflow in multiply below + } + + n := payloadBytes * int(pkt+1) + if n > maxPlaintext { + n = maxPlaintext + } + return n } // writeRecordLocked writes a TLS record with the given type and payload to the diff --git a/src/crypto/tls/conn_test.go b/src/crypto/tls/conn_test.go index 8334d90839cded..4e4bbc95e86d24 100644 --- a/src/crypto/tls/conn_test.go +++ b/src/crypto/tls/conn_test.go @@ -208,13 +208,10 @@ func runDynamicRecordSizingTest(t *testing.T, config *Config) { seenLargeRecord := false for i, size := range recordSizes { if !seenLargeRecord { - if size > tcpMSSEstimate { - if i < 100 { - t.Fatalf("Record #%d has size %d, which is too large too soon", i, size) - } - if size <= maxPlaintext { - t.Fatalf("Record #%d has odd size %d", i, size) - } + if size > (i+1)*tcpMSSEstimate { + t.Fatalf("Record #%d has size %d, which is too large too soon", i, size) + } + if size >= maxPlaintext { seenLargeRecord = true } } else if size <= maxPlaintext { diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 8e94f2143acbc4..cf617df19f5c00 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -284,10 +284,8 @@ func (hs *serverHandshakeState) checkForResumption() bool { return false } - if hs.sessionState.vers > hs.clientHello.vers { - return false - } - if vers, ok := c.config.mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { + // Never resume a session for a different TLS version. + if c.vers != hs.sessionState.vers { return false } diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go index fba81f619a6fe7..d878f9988949d7 100644 --- a/src/crypto/tls/handshake_server_test.go +++ b/src/crypto/tls/handshake_server_test.go @@ -399,6 +399,64 @@ func TestSCTHandshake(t *testing.T) { } } +func TestCrossVersionResume(t *testing.T) { + serverConfig := &Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Certificates: testConfig.Certificates, + } + clientConfig := &Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + InsecureSkipVerify: true, + ClientSessionCache: NewLRUClientSessionCache(1), + ServerName: "servername", + } + + // Establish a session at TLS 1.1. + clientConfig.MaxVersion = VersionTLS11 + _, _, err := testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + + // The client session cache now contains a TLS 1.1 session. + state, _, err := testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + if !state.DidResume { + t.Fatalf("handshake did not resume at the same version") + } + + // Test that the server will decline to resume at a lower version. + clientConfig.MaxVersion = VersionTLS10 + state, _, err = testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + if state.DidResume { + t.Fatalf("handshake resumed at a lower version") + } + + // The client session cache now contains a TLS 1.0 session. + state, _, err = testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + if !state.DidResume { + t.Fatalf("handshake did not resume at the same version") + } + + // Test that the server will decline to resume at a higher version. + clientConfig.MaxVersion = VersionTLS11 + state, _, err = testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + if state.DidResume { + t.Fatalf("handshake resumed at a higher version") + } +} + // Note: see comment in handshake_test.go for details of how the reference // tests work. diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go index 0be0b42912eeff..25dc386f53c8c9 100644 --- a/src/crypto/tls/tls.go +++ b/src/crypto/tls/tls.go @@ -170,10 +170,11 @@ func Dial(network, addr string, config *Config) (*Conn, error) { return DialWithDialer(new(net.Dialer), network, addr, config) } -// LoadX509KeyPair reads and parses a public/private key pair from a pair of -// files. The files must contain PEM encoded data. On successful return, -// Certificate.Leaf will be nil because the parsed form of the certificate is -// not retained. +// LoadX509KeyPair reads and parses a public/private key pair from a pair +// of files. The files must contain PEM encoded data. The certificate file +// may contain intermediate certificates following the leaf certificate to +// form a certificate chain. On successful return, Certificate.Leaf will +// be nil because the parsed form of the certificate is not retained. func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { certPEMBlock, err := ioutil.ReadFile(certFile) if err != nil { diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 1a33658a1e365b..894d7e82ab75c4 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -10,6 +10,7 @@ import ( "fmt" "internal/testenv" "io" + "math" "net" "strings" "testing" @@ -146,7 +147,7 @@ func TestX509MixedKeyPair(t *testing.T) { } } -func newLocalListener(t *testing.T) net.Listener { +func newLocalListener(t testing.TB) net.Listener { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { ln, err = net.Listen("tcp6", "[::1]:0") @@ -473,3 +474,156 @@ func (w *changeImplConn) Close() error { } return w.Conn.Close() } + +func throughput(b *testing.B, totalBytes int64, dynamicRecordSizingDisabled bool) { + ln := newLocalListener(b) + defer ln.Close() + + N := b.N + + var serr error + go func() { + for i := 0; i < N; i++ { + sconn, err := ln.Accept() + if err != nil { + serr = err + return + } + serverConfig := *testConfig + serverConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled + srv := Server(sconn, &serverConfig) + if err := srv.Handshake(); err != nil { + serr = fmt.Errorf("handshake: %v", err) + return + } + io.Copy(srv, srv) + } + }() + + b.SetBytes(totalBytes) + clientConfig := *testConfig + clientConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled + + buf := make([]byte, 1<<16) + chunks := int(math.Ceil(float64(totalBytes) / float64(len(buf)))) + for i := 0; i < N; i++ { + conn, err := Dial("tcp", ln.Addr().String(), &clientConfig) + if err != nil { + b.Fatal(err) + } + for j := 0; j < chunks; j++ { + _, err := conn.Write(buf) + if err != nil { + b.Fatal(err) + } + _, err = io.ReadFull(conn, buf) + if err != nil { + b.Fatal(err) + } + } + conn.Close() + } +} + +func BenchmarkThroughput(b *testing.B) { + for _, mode := range []string{"Max", "Dynamic"} { + for size := 1; size <= 64; size <<= 1 { + name := fmt.Sprintf("%sPacket/%dMB", mode, size) + b.Run(name, func(b *testing.B) { + throughput(b, int64(size<<20), mode == "Max") + }) + } + } +} + +type slowConn struct { + net.Conn + bps int +} + +func (c *slowConn) Write(p []byte) (int, error) { + if c.bps == 0 { + panic("too slow") + } + t0 := time.Now() + wrote := 0 + for wrote < len(p) { + time.Sleep(100 * time.Microsecond) + allowed := int(time.Since(t0).Seconds()*float64(c.bps)) / 8 + if allowed > len(p) { + allowed = len(p) + } + if wrote < allowed { + n, err := c.Conn.Write(p[wrote:allowed]) + wrote += n + if err != nil { + return wrote, err + } + } + } + return len(p), nil +} + +func latency(b *testing.B, bps int, dynamicRecordSizingDisabled bool) { + ln := newLocalListener(b) + defer ln.Close() + + N := b.N + + var serr error + go func() { + for i := 0; i < N; i++ { + sconn, err := ln.Accept() + if err != nil { + serr = err + return + } + serverConfig := *testConfig + serverConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled + srv := Server(&slowConn{sconn, bps}, &serverConfig) + if err := srv.Handshake(); err != nil { + serr = fmt.Errorf("handshake: %v", err) + return + } + io.Copy(srv, srv) + } + }() + + clientConfig := *testConfig + clientConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled + + buf := make([]byte, 16384) + peek := make([]byte, 1) + + for i := 0; i < N; i++ { + conn, err := Dial("tcp", ln.Addr().String(), &clientConfig) + if err != nil { + b.Fatal(err) + } + // make sure we're connected and previous connection has stopped + if _, err := conn.Write(buf[:1]); err != nil { + b.Fatal(err) + } + if _, err := io.ReadFull(conn, peek); err != nil { + b.Fatal(err) + } + if _, err := conn.Write(buf); err != nil { + b.Fatal(err) + } + if _, err = io.ReadFull(conn, peek); err != nil { + b.Fatal(err) + } + conn.Close() + } +} + +func BenchmarkLatency(b *testing.B) { + for _, mode := range []string{"Max", "Dynamic"} { + for _, kbps := range []int{200, 500, 1000, 2000, 5000} { + name := fmt.Sprintf("%sPacket/%dkbps", mode, kbps) + b.Run(name, func(b *testing.B) { + latency(b, kbps*1000, mode == "Max") + }) + } + } +} diff --git a/src/crypto/x509/root_cgo_darwin.go b/src/crypto/x509/root_cgo_darwin.go index f067cd7cf432fc..0e2fb357ee9040 100644 --- a/src/crypto/x509/root_cgo_darwin.go +++ b/src/crypto/x509/root_cgo_darwin.go @@ -21,41 +21,69 @@ package x509 // Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after // we've consumed its content. int FetchPEMRoots(CFDataRef *pemRoots) { - if (pemRoots == NULL) { - return -1; - } + // Get certificates from all domains, not just System, this lets + // the user add CAs to their "login" keychain, and Admins to add + // to the "System" keychain + SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem, + kSecTrustSettingsDomainAdmin, + kSecTrustSettingsDomainUser }; - CFArrayRef certs = NULL; - OSStatus err = SecTrustCopyAnchorCertificates(&certs); - if (err != noErr) { + int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain); + if (pemRoots == NULL) { return -1; } CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); - int i, ncerts = CFArrayGetCount(certs); - for (i = 0; i < ncerts; i++) { - CFDataRef data = NULL; - SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); - if (cert == NULL) { - continue; - } - - // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. - // Once we support weak imports via cgo we should prefer that, and fall back to this - // for older systems. - err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + for (int i = 0; i < numDomains; i++) { + CFArrayRef certs = NULL; + // Only get certificates from domain that are trusted + OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs); if (err != noErr) { continue; } - if (data != NULL) { - CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); - CFRelease(data); - } - } + int numCerts = CFArrayGetCount(certs); + for (int j = 0; j < numCerts; j++) { + CFDataRef data = NULL; + CFErrorRef errRef = NULL; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j); + if (cert == NULL) { + continue; + } + // We only want to add Root CAs, so make sure Subject and Issuer Name match + CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef); + if (errRef != NULL) { + CFRelease(errRef); + continue; + } + CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef); + if (errRef != NULL) { + CFRelease(subjectName); + CFRelease(errRef); + continue; + } + Boolean equal = CFEqual(subjectName, issuerName); + CFRelease(subjectName); + CFRelease(issuerName); + if (!equal) { + continue; + } - CFRelease(certs); + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + if (err != noErr) { + continue; + } + if (data != NULL) { + CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } + } + CFRelease(certs); + } *pemRoots = combinedData; return 0; } diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 6004d5cd239d18..9e6d67df55fc94 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -1853,8 +1853,8 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error) return ret, nil } -// CreateCertificateRequest creates a new certificate based on a template. The -// following members of template are used: Subject, Attributes, +// CreateCertificateRequest creates a new certificate request based on a template. +// The following members of template are used: Subject, Attributes, // SignatureAlgorithm, Extensions, DNSNames, EmailAddresses, and IPAddresses. // The private key is the private key of the signer. // diff --git a/src/debug/elf/file_test.go b/src/debug/elf/file_test.go index 2fe6febb26b024..b189219a556a21 100644 --- a/src/debug/elf/file_test.go +++ b/src/debug/elf/file_test.go @@ -655,7 +655,7 @@ func TestCompressedSection(t *testing.T) { // Test Open method and seeking. buf, have, count := make([]byte, len(b)), make([]bool, len(b)), 0 sf := sec.Open() - if got, err := sf.Seek(0, 2); got != int64(len(b)) || err != nil { + if got, err := sf.Seek(0, io.SeekEnd); got != int64(len(b)) || err != nil { t.Fatalf("want seek end %d, got %d error %v", len(b), got, err) } if n, err := sf.Read(buf); n != 0 || err != io.EOF { @@ -668,11 +668,11 @@ func TestCompressedSection(t *testing.T) { target := rand.Int63n(int64(len(buf))) var offset int64 switch whence { - case 0: + case io.SeekStart: offset = target - case 1: + case io.SeekCurrent: offset = target - pos - case 2: + case io.SeekEnd: offset = target - int64(len(buf)) } pos, err = sf.Seek(offset, whence) diff --git a/src/debug/elf/reader.go b/src/debug/elf/reader.go index 4dac6d1b2973c5..eab437318d6d3e 100644 --- a/src/debug/elf/reader.go +++ b/src/debug/elf/reader.go @@ -63,11 +63,11 @@ func (r *readSeekerFromReader) Read(p []byte) (n int, err error) { func (r *readSeekerFromReader) Seek(offset int64, whence int) (int64, error) { var newOffset int64 switch whence { - case 0: + case io.SeekStart: newOffset = offset - case 1: + case io.SeekCurrent: newOffset = r.offset + offset - case 2: + case io.SeekEnd: newOffset = r.size + offset default: return 0, os.ErrInvalid diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go index 1a780bf121ac4c..9f82e31ae4170a 100644 --- a/src/debug/gosym/pclntab_test.go +++ b/src/debug/gosym/pclntab_test.go @@ -5,6 +5,7 @@ package gosym import ( + "bytes" "debug/elf" "internal/testenv" "io/ioutil" @@ -42,6 +43,21 @@ func dotest(t *testing.T) { if err := cmd.Run(); err != nil { t.Fatal(err) } + + // stamp .o file as being 'package main' so that go tool link will accept it + data, err := ioutil.ReadFile(pclinetestBinary + ".o") + if err != nil { + t.Fatal(err) + } + i := bytes.IndexByte(data, '\n') + if i < 0 { + t.Fatal("bad binary") + } + data = append(append(data[:i:i], "\nmain"...), data[i:]...) + if err := ioutil.WriteFile(pclinetestBinary+".o", data, 0666); err != nil { + t.Fatal(err) + } + cmd = exec.Command("go", "tool", "link", "-H", "linux", "-o", pclinetestBinary, pclinetestBinary+".o") cmd.Stdout = os.Stdout diff --git a/src/debug/gosym/symtab.go b/src/debug/gosym/symtab.go index c8fa9a0b38417f..f5f99630950916 100644 --- a/src/debug/gosym/symtab.go +++ b/src/debug/gosym/symtab.go @@ -40,8 +40,13 @@ func (s *Sym) Static() bool { return s.Type >= 'a' } // PackageName returns the package part of the symbol name, // or the empty string if there is none. func (s *Sym) PackageName() string { - if i := strings.Index(s.Name, "."); i != -1 { - return s.Name[0:i] + pathend := strings.LastIndex(s.Name, "/") + if pathend < 0 { + pathend = 0 + } + + if i := strings.Index(s.Name[pathend:], "."); i != -1 { + return s.Name[:pathend+i] } return "" } @@ -49,12 +54,16 @@ func (s *Sym) PackageName() string { // ReceiverName returns the receiver type name of this symbol, // or the empty string if there is none. func (s *Sym) ReceiverName() string { - l := strings.Index(s.Name, ".") - r := strings.LastIndex(s.Name, ".") + pathend := strings.LastIndex(s.Name, "/") + if pathend < 0 { + pathend = 0 + } + l := strings.Index(s.Name[pathend:], ".") + r := strings.LastIndex(s.Name[pathend:], ".") if l == -1 || r == -1 || l == r { return "" } - return s.Name[l+1 : r] + return s.Name[pathend+l+1 : pathend+r] } // BaseName returns the symbol name without the package or receiver name. diff --git a/src/debug/gosym/symtab_test.go b/src/debug/gosym/symtab_test.go new file mode 100644 index 00000000000000..08e86336b8eb67 --- /dev/null +++ b/src/debug/gosym/symtab_test.go @@ -0,0 +1,43 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gosym + +import ( + "fmt" + "testing" +) + +func assertString(t *testing.T, dsc, out, tgt string) { + if out != tgt { + t.Fatalf("Expected: %q Actual: %q for %s", tgt, out, dsc) + } +} + +func TestStandardLibPackage(t *testing.T) { + s1 := Sym{Name: "io.(*LimitedReader).Read"} + s2 := Sym{Name: "io.NewSectionReader"} + assertString(t, fmt.Sprintf("package of %q", s1.Name), s1.PackageName(), "io") + assertString(t, fmt.Sprintf("package of %q", s2.Name), s2.PackageName(), "io") + assertString(t, fmt.Sprintf("receiver of %q", s1.Name), s1.ReceiverName(), "(*LimitedReader)") + assertString(t, fmt.Sprintf("receiver of %q", s2.Name), s2.ReceiverName(), "") +} + +func TestStandardLibPathPackage(t *testing.T) { + s1 := Sym{Name: "debug/gosym.(*LineTable).PCToLine"} + s2 := Sym{Name: "debug/gosym.NewTable"} + assertString(t, fmt.Sprintf("package of %q", s1.Name), s1.PackageName(), "debug/gosym") + assertString(t, fmt.Sprintf("package of %q", s2.Name), s2.PackageName(), "debug/gosym") + assertString(t, fmt.Sprintf("receiver of %q", s1.Name), s1.ReceiverName(), "(*LineTable)") + assertString(t, fmt.Sprintf("receiver of %q", s2.Name), s2.ReceiverName(), "") +} + +func TestRemotePackage(t *testing.T) { + s1 := Sym{Name: "github.com/docker/doc.ker/pkg/mflag.(*FlagSet).PrintDefaults"} + s2 := Sym{Name: "github.com/docker/doc.ker/pkg/mflag.PrintDefaults"} + assertString(t, fmt.Sprintf("package of %q", s1.Name), s1.PackageName(), "github.com/docker/doc.ker/pkg/mflag") + assertString(t, fmt.Sprintf("package of %q", s2.Name), s2.PackageName(), "github.com/docker/doc.ker/pkg/mflag") + assertString(t, fmt.Sprintf("receiver of %q", s1.Name), s1.ReceiverName(), "(*FlagSet)") + assertString(t, fmt.Sprintf("receiver of %q", s2.Name), s2.ReceiverName(), "") +} diff --git a/src/encoding/csv/reader.go b/src/encoding/csv/reader.go index 58f6eed1e66c3f..89283bb3031d4a 100644 --- a/src/encoding/csv/reader.go +++ b/src/encoding/csv/reader.go @@ -3,6 +3,8 @@ // license that can be found in the LICENSE file. // Package csv reads and writes comma-separated values (CSV) files. +// There are many kinds of CSV files; this package supports the format +// described in RFC 4180. // // A csv file contains zero or more records of one or more fields per record. // Each record is separated by the newline character. The final record may @@ -234,7 +236,7 @@ func (r *Reader) parseRecord() (fields []string, err error) { for { haveField, delim, err := r.parseField() if haveField { - // If FieldsPerRecord is greater then 0 we can assume the final + // If FieldsPerRecord is greater than 0 we can assume the final // length of fields to be equal to FieldsPerRecord. if r.FieldsPerRecord > 0 && fields == nil { fields = make([]string, 0, r.FieldsPerRecord) diff --git a/src/encoding/gob/codec_test.go b/src/encoding/gob/codec_test.go index b772171f930adc..d4002cbccab3d3 100644 --- a/src/encoding/gob/codec_test.go +++ b/src/encoding/gob/codec_test.go @@ -1253,7 +1253,7 @@ func TestIgnoreInterface(t *testing.T) { if item2.I != item1.I { t.Error("normal int did not decode correctly") } - if item2.F != item2.F { + if item2.F != item1.F { t.Error("normal float did not decode correctly") } } @@ -1280,7 +1280,7 @@ func TestUnexportedFields(t *testing.T) { if err != nil { t.Fatal("decode error:", err) } - if u0.A != u0.A || u0.B != u1.B || u0.D != u1.D { + if u0.A != u1.A || u0.B != u1.B || u0.D != u1.D { t.Errorf("u1->u0: expected %v; got %v", u0, u1) } if u1.c != 1234. { diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 434edf8ea45096..2eda875bfdee8a 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -62,10 +62,10 @@ import ( // the additional Go array elements are set to zero values. // // To unmarshal a JSON object into a map, Unmarshal first establishes a map to -// use, If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal +// use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal // reuses the existing map, keeping existing entries. Unmarshal then stores key- -// value pairs from the JSON object into the map. The map's key type must -// either be a string or implement encoding.TextUnmarshaler. +// value pairs from the JSON object into the map. The map's key type must +// either be a string, an integer, or implement encoding.TextUnmarshaler. // // If a JSON value is not appropriate for a given target type, // or if a JSON number overflows the target type, Unmarshal @@ -581,17 +581,24 @@ func (d *decodeState) object(v reflect.Value) { // Check type of target: // struct or - // map[string]T or map[encoding.TextUnmarshaler]T + // map[T1]T2 where T1 is string, an integer type, + // or an encoding.TextUnmarshaler switch v.Kind() { case reflect.Map: - // Map key must either have string kind or be an encoding.TextUnmarshaler. + // Map key must either have string kind, have an integer kind, + // or be an encoding.TextUnmarshaler. t := v.Type() - if t.Key().Kind() != reflect.String && - !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return + switch t.Key().Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + default: + if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } } if v.IsNil() { v.Set(reflect.MakeMap(t)) @@ -696,13 +703,32 @@ func (d *decodeState) object(v reflect.Value) { var kv reflect.Value switch { case kt.Kind() == reflect.String: - kv = reflect.ValueOf(key).Convert(v.Type().Key()) + kv = reflect.ValueOf(key).Convert(kt) case reflect.PtrTo(kt).Implements(textUnmarshalerType): kv = reflect.New(v.Type().Key()) d.literalStore(item, kv, true) kv = kv.Elem() default: - panic("json: Unexpected key type") // should never occur + switch kt.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := string(key) + n, err := strconv.ParseInt(s, 10, 64) + if err != nil || reflect.Zero(kt).OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)}) + return + } + kv = reflect.ValueOf(n).Convert(kt) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + s := string(key) + n, err := strconv.ParseUint(s, 10, 64) + if err != nil || reflect.Zero(kt).OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)}) + return + } + kv = reflect.ValueOf(n).Convert(kt) + default: + panic("json: Unexpected key type") // should never occur + } } v.SetMapIndex(kv, subv) } diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index 30e46ca44f07bd..255ff5c66a75bf 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -10,8 +10,10 @@ import ( "errors" "fmt" "image" + "math" "net" "reflect" + "strconv" "strings" "testing" "time" @@ -53,6 +55,8 @@ type tx struct { x int } +type u8 uint8 + // A type that can unmarshal itself. type unmarshaler struct { @@ -92,6 +96,29 @@ type ustructText struct { M unmarshalerText } +// u8marshal is an integer type that can marshal/unmarshal itself. +type u8marshal uint8 + +func (u8 u8marshal) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("u%d", u8)), nil +} + +var errMissingU8Prefix = errors.New("missing 'u' prefix") + +func (u8 *u8marshal) UnmarshalText(b []byte) error { + if !bytes.HasPrefix(b, []byte{'u'}) { + return errMissingU8Prefix + } + n, err := strconv.Atoi(string(b[1:])) + if err != nil { + return err + } + *u8 = u8marshal(n) + return nil +} + +var _ encoding.TextUnmarshaler = (*u8marshal)(nil) + var ( um0, um1 unmarshaler // target2 of unmarshaling ump = &um1 @@ -211,14 +238,6 @@ type S13 struct { S8 } -type unmarshalTest struct { - in string - ptr interface{} - out interface{} - err error - useNumber bool -} - type Ambig struct { // Given "hello", the first match should win. First int `json:"HELLO"` @@ -234,6 +253,127 @@ type XYZ struct { func sliceAddr(x []int) *[]int { return &x } func mapAddr(x map[string]int) *map[string]int { return &x } +type byteWithMarshalJSON byte + +func (b byteWithMarshalJSON) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"Z%.2x"`, byte(b))), nil +} + +func (b *byteWithMarshalJSON) UnmarshalJSON(data []byte) error { + if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' { + return fmt.Errorf("bad quoted string") + } + i, err := strconv.ParseInt(string(data[2:4]), 16, 8) + if err != nil { + return fmt.Errorf("bad hex") + } + *b = byteWithMarshalJSON(i) + return nil +} + +type byteWithPtrMarshalJSON byte + +func (b *byteWithPtrMarshalJSON) MarshalJSON() ([]byte, error) { + return byteWithMarshalJSON(*b).MarshalJSON() +} + +func (b *byteWithPtrMarshalJSON) UnmarshalJSON(data []byte) error { + return (*byteWithMarshalJSON)(b).UnmarshalJSON(data) +} + +type byteWithMarshalText byte + +func (b byteWithMarshalText) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf(`Z%.2x`, byte(b))), nil +} + +func (b *byteWithMarshalText) UnmarshalText(data []byte) error { + if len(data) != 3 || data[0] != 'Z' { + return fmt.Errorf("bad quoted string") + } + i, err := strconv.ParseInt(string(data[1:3]), 16, 8) + if err != nil { + return fmt.Errorf("bad hex") + } + *b = byteWithMarshalText(i) + return nil +} + +type byteWithPtrMarshalText byte + +func (b *byteWithPtrMarshalText) MarshalText() ([]byte, error) { + return byteWithMarshalText(*b).MarshalText() +} + +func (b *byteWithPtrMarshalText) UnmarshalText(data []byte) error { + return (*byteWithMarshalText)(b).UnmarshalText(data) +} + +type intWithMarshalJSON int + +func (b intWithMarshalJSON) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"Z%.2x"`, int(b))), nil +} + +func (b *intWithMarshalJSON) UnmarshalJSON(data []byte) error { + if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' { + return fmt.Errorf("bad quoted string") + } + i, err := strconv.ParseInt(string(data[2:4]), 16, 8) + if err != nil { + return fmt.Errorf("bad hex") + } + *b = intWithMarshalJSON(i) + return nil +} + +type intWithPtrMarshalJSON int + +func (b *intWithPtrMarshalJSON) MarshalJSON() ([]byte, error) { + return intWithMarshalJSON(*b).MarshalJSON() +} + +func (b *intWithPtrMarshalJSON) UnmarshalJSON(data []byte) error { + return (*intWithMarshalJSON)(b).UnmarshalJSON(data) +} + +type intWithMarshalText int + +func (b intWithMarshalText) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf(`Z%.2x`, int(b))), nil +} + +func (b *intWithMarshalText) UnmarshalText(data []byte) error { + if len(data) != 3 || data[0] != 'Z' { + return fmt.Errorf("bad quoted string") + } + i, err := strconv.ParseInt(string(data[1:3]), 16, 8) + if err != nil { + return fmt.Errorf("bad hex") + } + *b = intWithMarshalText(i) + return nil +} + +type intWithPtrMarshalText int + +func (b *intWithPtrMarshalText) MarshalText() ([]byte, error) { + return intWithMarshalText(*b).MarshalText() +} + +func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error { + return (*intWithMarshalText)(b).UnmarshalText(data) +} + +type unmarshalTest struct { + in string + ptr interface{} + out interface{} + err error + useNumber bool + golden bool +} + var unmarshalTests = []unmarshalTest{ // basic types {in: `true`, ptr: new(bool), out: true}, @@ -320,7 +460,69 @@ var unmarshalTests = []unmarshalTest{ {in: `["x:y"]`, ptr: &umslicepType, out: &umsliceXY}, {in: `{"M":"x:y"}`, ptr: umstructType, out: umstructXY}, - // Map keys can be encoding.TextUnmarshalers + // integer-keyed map test + { + in: `{"-1":"a","0":"b","1":"c"}`, + ptr: new(map[int]string), + out: map[int]string{-1: "a", 0: "b", 1: "c"}, + }, + { + in: `{"0":"a","10":"c","9":"b"}`, + ptr: new(map[u8]string), + out: map[u8]string{0: "a", 9: "b", 10: "c"}, + }, + { + in: `{"-9223372036854775808":"min","9223372036854775807":"max"}`, + ptr: new(map[int64]string), + out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"}, + }, + { + in: `{"18446744073709551615":"max"}`, + ptr: new(map[uint64]string), + out: map[uint64]string{math.MaxUint64: "max"}, + }, + { + in: `{"0":false,"10":true}`, + ptr: new(map[uintptr]bool), + out: map[uintptr]bool{0: false, 10: true}, + }, + + // Check that MarshalText and UnmarshalText take precedence + // over default integer handling in map keys. + { + in: `{"u2":4}`, + ptr: new(map[u8marshal]int), + out: map[u8marshal]int{2: 4}, + }, + { + in: `{"2":4}`, + ptr: new(map[u8marshal]int), + err: errMissingU8Prefix, + }, + + // integer-keyed map errors + { + in: `{"abc":"abc"}`, + ptr: new(map[int]string), + err: &UnmarshalTypeError{"number abc", reflect.TypeOf(0), 2}, + }, + { + in: `{"256":"abc"}`, + ptr: new(map[uint8]string), + err: &UnmarshalTypeError{"number 256", reflect.TypeOf(uint8(0)), 2}, + }, + { + in: `{"128":"abc"}`, + ptr: new(map[int8]string), + err: &UnmarshalTypeError{"number 128", reflect.TypeOf(int8(0)), 2}, + }, + { + in: `{"-1":"abc"}`, + ptr: new(map[uint8]string), + err: &UnmarshalTypeError{"number -1", reflect.TypeOf(uint8(0)), 2}, + }, + + // Map keys can be encoding.TextUnmarshalers. {in: `{"x:y":true}`, ptr: &ummapType, out: ummapXY}, // If multiple values for the same key exists, only the most recent value is used. {in: `{"x:y":false,"x:y":true}`, ptr: &ummapType, out: ummapXY}, @@ -458,6 +660,84 @@ var unmarshalTests = []unmarshalTest{ ptr: &map[unmarshaler]string{}, err: &UnmarshalTypeError{"object", reflect.TypeOf(map[unmarshaler]string{}), 1}, }, + + // related to issue 13783. + // Go 1.7 changed marshaling a slice of typed byte to use the methods on the byte type, + // similar to marshaling a slice of typed int. + // These tests check that, assuming the byte type also has valid decoding methods, + // either the old base64 string encoding or the new per-element encoding can be + // successfully unmarshaled. The custom unmarshalers were accessible in earlier + // versions of Go, even though the custom marshaler was not. + { + in: `"AQID"`, + ptr: new([]byteWithMarshalJSON), + out: []byteWithMarshalJSON{1, 2, 3}, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]byteWithMarshalJSON), + out: []byteWithMarshalJSON{1, 2, 3}, + golden: true, + }, + { + in: `"AQID"`, + ptr: new([]byteWithMarshalText), + out: []byteWithMarshalText{1, 2, 3}, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]byteWithMarshalText), + out: []byteWithMarshalText{1, 2, 3}, + golden: true, + }, + { + in: `"AQID"`, + ptr: new([]byteWithPtrMarshalJSON), + out: []byteWithPtrMarshalJSON{1, 2, 3}, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]byteWithPtrMarshalJSON), + out: []byteWithPtrMarshalJSON{1, 2, 3}, + golden: true, + }, + { + in: `"AQID"`, + ptr: new([]byteWithPtrMarshalText), + out: []byteWithPtrMarshalText{1, 2, 3}, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]byteWithPtrMarshalText), + out: []byteWithPtrMarshalText{1, 2, 3}, + golden: true, + }, + + // ints work with the marshaler but not the base64 []byte case + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]intWithMarshalJSON), + out: []intWithMarshalJSON{1, 2, 3}, + golden: true, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]intWithMarshalText), + out: []intWithMarshalText{1, 2, 3}, + golden: true, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]intWithPtrMarshalJSON), + out: []intWithPtrMarshalJSON{1, 2, 3}, + golden: true, + }, + { + in: `["Z01","Z02","Z03"]`, + ptr: new([]intWithPtrMarshalText), + out: []intWithPtrMarshalText{1, 2, 3}, + golden: true, + }, } func TestMarshal(t *testing.T) { @@ -591,13 +871,16 @@ func TestUnmarshal(t *testing.T) { continue } - // Check round trip. + // Check round trip also decodes correctly. if tt.err == nil { enc, err := Marshal(v.Interface()) if err != nil { t.Errorf("#%d: error re-marshaling: %v", i, err) continue } + if tt.golden && !bytes.Equal(enc, in) { + t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in) + } vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) dec = NewDecoder(bytes.NewReader(enc)) if tt.useNumber { diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index d8c779869b33f8..3917084dc3324a 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -117,9 +117,13 @@ import ( // an anonymous struct field in both current and earlier versions, give the field // a JSON tag of "-". // -// Map values encode as JSON objects. The map's key type must either be a string -// or implement encoding.TextMarshaler. The map keys are used as JSON object -// keys, subject to the UTF-8 coercion described for string values above. +// Map values encode as JSON objects. The map's key type must either be a +// string, an integer type, or implement encoding.TextMarshaler. The map keys +// are sorted and used as JSON object keys by applying the following rules, +// subject to the UTF-8 coercion described for string values above: +// - string keys are used directly +// - encoding.TextMarshalers are marshaled +// - integer keys are converted to strings // // Pointer values encode as the value pointed to. // A nil pointer encodes as the null JSON value. @@ -644,8 +648,14 @@ func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { } func newMapEncoder(t reflect.Type) encoderFunc { - if t.Key().Kind() != reflect.String && !t.Key().Implements(textMarshalerType) { - return unsupportedTypeEncoder + switch t.Key().Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + default: + if !t.Key().Implements(textMarshalerType) { + return unsupportedTypeEncoder + } } me := &mapEncoder{typeEncoder(t.Elem())} return me.encode @@ -688,10 +698,11 @@ func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { func newSliceEncoder(t reflect.Type) encoderFunc { // Byte slices get special treatment; arrays don't. - if t.Elem().Kind() == reflect.Uint8 && - !t.Elem().Implements(marshalerType) && - !t.Elem().Implements(textMarshalerType) { - return encodeByteSlice + if t.Elem().Kind() == reflect.Uint8 { + p := reflect.PtrTo(t.Elem()) + if !p.Implements(marshalerType) && !p.Implements(textMarshalerType) { + return encodeByteSlice + } } enc := &sliceEncoder{newArrayEncoder(t)} return enc.encode @@ -806,9 +817,20 @@ func (w *reflectWithString) resolve() error { w.s = w.v.String() return nil } - buf, err := w.v.Interface().(encoding.TextMarshaler).MarshalText() - w.s = string(buf) - return err + if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok { + buf, err := tm.MarshalText() + w.s = string(buf) + return err + } + switch w.v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + w.s = strconv.FormatInt(w.v.Int(), 10) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + w.s = strconv.FormatUint(w.v.Uint(), 10) + return nil + } + panic("unexpected map key type") } // byString is a slice of reflectWithString where the reflect.Value is either diff --git a/src/encoding/json/example_test.go b/src/encoding/json/example_test.go index 326bdc9540ef9b..555eff93c0ec38 100644 --- a/src/encoding/json/example_test.go +++ b/src/encoding/json/example_test.go @@ -143,10 +143,9 @@ func ExampleDecoder_Decode_stream() { } fmt.Printf("%T: %v\n", t, t) - var m Message // while the array contains values for dec.More() { - + var m Message // decode an array value (Message) err := dec.Decode(&m) if err != nil { diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go index d6b2992e9be1c3..87f0e57c6cdb5c 100644 --- a/src/encoding/json/stream.go +++ b/src/encoding/json/stream.go @@ -204,7 +204,10 @@ func (enc *Encoder) Encode(v interface{}) error { e.WriteByte('\n') b := e.Bytes() - if enc.indentBuf != nil { + if enc.indentPrefix != "" || enc.indentValue != "" { + if enc.indentBuf == nil { + enc.indentBuf = new(bytes.Buffer) + } enc.indentBuf.Reset() err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue) if err != nil { @@ -219,17 +222,23 @@ func (enc *Encoder) Encode(v interface{}) error { return err } -// Indent sets the encoder to format each encoded value with Indent. -func (enc *Encoder) Indent(prefix, indent string) { - enc.indentBuf = new(bytes.Buffer) +// SetIndent instructs the encoder to format each subsequent encoded +// value as if indented by the package-level function Indent(dst, src, prefix, indent). +// Calling SetIndent("", "") disables indentation. +func (enc *Encoder) SetIndent(prefix, indent string) { enc.indentPrefix = prefix enc.indentValue = indent } -// DisableHTMLEscaping causes the encoder not to escape angle brackets -// ("<" and ">") or ampersands ("&") in JSON strings. -func (enc *Encoder) DisableHTMLEscaping() { - enc.escapeHTML = false +// SetEscapeHTML specifies whether problematic HTML characters +// should be escaped inside JSON quoted strings. +// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e +// to avoid certain safety problems that can arise when embedding JSON in HTML. +// +// In non-HTML settings where the escaping interferes with the readability +// of the output, SetEscapeHTML(false) disables this behavior. +func (enc *Encoder) SetEscapeHTML(on bool) { + enc.escapeHTML = on } // RawMessage is a raw encoded JSON value. diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go index 3516ac3b83d971..84edeb187c2592 100644 --- a/src/encoding/json/stream_test.go +++ b/src/encoding/json/stream_test.go @@ -44,6 +44,9 @@ func TestEncoder(t *testing.T) { for i := 0; i <= len(streamTest); i++ { var buf bytes.Buffer enc := NewEncoder(&buf) + // Check that enc.SetIndent("", "") turns off indentation. + enc.SetIndent(">", ".") + enc.SetIndent("", "") for j, v := range streamTest[0:i] { if err := enc.Encode(v); err != nil { t.Fatalf("encode #%d: %v", j, err) @@ -77,7 +80,7 @@ false func TestEncoderIndent(t *testing.T) { var buf bytes.Buffer enc := NewEncoder(&buf) - enc.Indent(">", ".") + enc.SetIndent(">", ".") for _, v := range streamTest { enc.Encode(v) } @@ -87,7 +90,7 @@ func TestEncoderIndent(t *testing.T) { } } -func TestEncoderDisableHTMLEscaping(t *testing.T) { +func TestEncoderSetEscapeHTML(t *testing.T) { var c C var ct CText for _, tt := range []struct { @@ -109,12 +112,12 @@ func TestEncoderDisableHTMLEscaping(t *testing.T) { t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape) } buf.Reset() - enc.DisableHTMLEscaping() + enc.SetEscapeHTML(false) if err := enc.Encode(tt.v); err != nil { - t.Fatalf("DisableHTMLEscaping Encode(%s): %s", tt.name, err) + t.Fatalf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err) } if got := strings.TrimSpace(buf.String()); got != tt.want { - t.Errorf("DisableHTMLEscaping Encode(%s) = %#q, want %#q", + t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q", tt.name, got, tt.want) } } diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go index b7ea433014b9dc..d5465c518f81d9 100644 --- a/src/expvar/expvar.go +++ b/src/expvar/expvar.go @@ -39,6 +39,8 @@ import ( // Var is an abstract type for all exported variables. type Var interface { // String returns a valid JSON value for the variable. + // Types with String methods that do not return valid JSON + // (such as time.Time) must not be used as a Var. String() string } diff --git a/src/fmt/doc.go b/src/fmt/doc.go index fefc10c19de8ad..c312914b44a48b 100644 --- a/src/fmt/doc.go +++ b/src/fmt/doc.go @@ -210,7 +210,7 @@ Too many arguments: %!(EXTRA type=value) Printf("hi", "guys"): hi%!(EXTRA string=guys) Too few arguments: %!verb(MISSING) - Printf("hi%d"): hi %!d(MISSING) + Printf("hi%d"): hi%!d(MISSING) Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC) Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi diff --git a/src/go/build/build.go b/src/go/build/build.go index fa258d3dc671a4..9706b8b6b31d62 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -1151,7 +1151,7 @@ func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binary } line = bytes.TrimSpace(line) if bytes.HasPrefix(line, slashslash) { - if bytes.HasPrefix(line, binaryOnlyComment) { + if bytes.Equal(line, binaryOnlyComment) { sawBinaryOnly = true } line = bytes.TrimSpace(line[len(slashslash):]) diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 8a8c4be2174af0..f9a428edd42b45 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -136,7 +136,19 @@ var pkgDeps = map[string][]string{ "internal/syscall/unix": {"L0", "syscall"}, "internal/syscall/windows": {"L0", "syscall", "internal/syscall/windows/sysdll"}, "internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"}, - "time": {"L0", "syscall", "internal/syscall/windows/registry"}, + "time": { + // "L0" without the "io" package: + "errors", + "runtime", + "runtime/internal/atomic", + "sync", + "sync/atomic", + "unsafe", + // Other time dependencies: + "internal/syscall/windows/registry", + "syscall", + }, + "os": {"L1", "os", "syscall", "time", "internal/syscall/windows"}, "path/filepath": {"L2", "os", "syscall"}, "io/ioutil": {"L2", "os", "path/filepath", "time"}, @@ -280,10 +292,13 @@ var pkgDeps = map[string][]string{ // Basic networking. // Because net must be used by any package that wants to // do networking portably, it must have a small dependency set: just L0+basic os. - "net": {"L0", "CGO", + "net": { + "L0", "CGO", "context", "math/rand", "os", "sort", "syscall", "time", "internal/nettrace", - "internal/syscall/windows", "internal/singleflight", "internal/race"}, + "internal/syscall/windows", "internal/singleflight", "internal/race", + "golang.org/x/net/route", + }, // NET enables use of basic network-related packages. "NET": { @@ -364,6 +379,7 @@ var pkgDeps = map[string][]string{ "mime/multipart", "runtime/debug", "net/http/internal", "golang.org/x/net/http2/hpack", + "golang.org/x/net/lex/httplex", "internal/nettrace", "net/http/httptrace", }, diff --git a/src/go/internal/gccgoimporter/importer.go b/src/go/internal/gccgoimporter/importer.go index aa0d01afdf3826..19b9c73568605f 100644 --- a/src/go/internal/gccgoimporter/importer.go +++ b/src/go/internal/gccgoimporter/importer.go @@ -88,12 +88,6 @@ func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err e if err != nil { return } - // reset to offset 0 - needed on Plan 9 (see issue #11265) - // TODO: remove once issue #11265 has been resolved. - _, err = f.Seek(0, 0) - if err != nil { - return - } var elfreader io.ReaderAt switch string(magic[:]) { @@ -168,7 +162,7 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo if err != nil { return } - _, err = reader.Seek(0, 0) + _, err = reader.Seek(0, io.SeekStart) if err != nil { return } diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index eb29df77ab757b..341358287a1ffd 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -23,9 +23,10 @@ type importer struct { buf []byte // for reading strings // object lists - strList []string // in order of appearance - pkgList []*types.Package // in order of appearance - typList []types.Type // in order of appearance + strList []string // in order of appearance + pkgList []*types.Package // in order of appearance + typList []types.Type // in order of appearance + trackAllTypes bool // position encoding posInfoFormat bool @@ -59,6 +60,8 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format) } + p.trackAllTypes = p.rawByte() == 'a' + p.posInfoFormat = p.int() != 0 // --- generic export data --- @@ -93,7 +96,12 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i // complete interfaces for _, typ := range p.typList { - if it, ok := typ.(*types.Interface); ok { + // If we only record named types (!p.trackAllTypes), + // we must check the underlying types here. If we + // track all types, the Underlying() method call is + // not needed. + // TODO(gri) Remove if p.trackAllTypes is gone. + if it, ok := typ.Underlying().(*types.Interface); ok { it.Complete() } } @@ -162,7 +170,7 @@ func (p *importer) declare(obj types.Object) { // imported. // (See also the comment in cmd/compile/internal/gc/bimport.go importer.obj, // switch case importing functions). - panic(fmt.Sprintf("%s already declared", alt.Name())) + panic(fmt.Sprintf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", alt, obj)) } } @@ -304,7 +312,9 @@ func (p *importer) typ(parent *types.Package) types.Type { case arrayTag: t := new(types.Array) - p.record(t) + if p.trackAllTypes { + p.record(t) + } n := p.int64() *t = *types.NewArray(p.typ(parent), n) @@ -312,35 +322,45 @@ func (p *importer) typ(parent *types.Package) types.Type { case sliceTag: t := new(types.Slice) - p.record(t) + if p.trackAllTypes { + p.record(t) + } *t = *types.NewSlice(p.typ(parent)) return t case dddTag: t := new(dddSlice) - p.record(t) + if p.trackAllTypes { + p.record(t) + } t.elem = p.typ(parent) return t case structTag: t := new(types.Struct) - p.record(t) + if p.trackAllTypes { + p.record(t) + } *t = *types.NewStruct(p.fieldList(parent)) return t case pointerTag: t := new(types.Pointer) - p.record(t) + if p.trackAllTypes { + p.record(t) + } *t = *types.NewPointer(p.typ(parent)) return t case signatureTag: t := new(types.Signature) - p.record(t) + if p.trackAllTypes { + p.record(t) + } params, isddd := p.paramList() result, _ := p.paramList() @@ -353,7 +373,9 @@ func (p *importer) typ(parent *types.Package) types.Type { // such cycle must contain a named type which would have been // first defined earlier. n := len(p.typList) - p.record(nil) + if p.trackAllTypes { + p.record(nil) + } // no embedded interfaces with gc compiler if p.int() != 0 { @@ -361,12 +383,16 @@ func (p *importer) typ(parent *types.Package) types.Type { } t := types.NewInterface(p.methodList(parent), nil) - p.typList[n] = t + if p.trackAllTypes { + p.typList[n] = t + } return t case mapTag: t := new(types.Map) - p.record(t) + if p.trackAllTypes { + p.record(t) + } key := p.typ(parent) val := p.typ(parent) @@ -375,7 +401,9 @@ func (p *importer) typ(parent *types.Package) types.Type { case chanTag: t := new(types.Chan) - p.record(t) + if p.trackAllTypes { + p.record(t) + } var dir types.ChanDir // tag values must match the constants in cmd/compile/internal/gc/go.go diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index b2848c3023696a..2c6e676225fb14 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -163,7 +163,7 @@ func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types var data []byte data, err = ioutil.ReadAll(buf) if err == nil { - _, pkg, err = BImportData(packages, data, path) + _, pkg, err = BImportData(packages, data, id) return } default: diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index e56720b0d52543..8de36c713c1453 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -351,9 +351,9 @@ func TestIssue13898(t *testing.T) { } // lookup go/types.Object.Pkg method - m, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Pkg") + m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") if m == nil { - t.Fatal("go/types.Object.Pkg not found") + t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) } // the method must belong to go/types @@ -361,3 +361,42 @@ func TestIssue13898(t *testing.T) { t.Fatalf("found %v; want go/types", m.Pkg()) } } + +func TestIssue15517(t *testing.T) { + skipSpecialPlatforms(t) + + // This package only handles gc export data. + if runtime.Compiler != "gc" { + t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) + return + } + + // On windows, we have to set the -D option for the compiler to avoid having a drive + // letter and an illegal ':' in the import path - just skip it (see also issue #3483). + if runtime.GOOS == "windows" { + t.Skip("avoid dealing with relative paths/drive letters on windows") + } + + if f := compile(t, "testdata", "p.go"); f != "" { + defer os.Remove(f) + } + + // Multiple imports of p must succeed without redeclaration errors. + // We use an import path that's not cleaned up so that the eventual + // file path for the package is different from the package path; this + // will expose the error if it is present. + // + // (Issue: Both the textual and the binary importer used the file path + // of the package to be imported as key into the shared packages map. + // However, the binary importer then used the package path to identify + // the imported package to mark it as complete; effectively marking the + // wrong package as complete. By using an "unclean" package path, the + // file and package path are different, exposing the problem if present. + // The same issue occurs with vendoring.) + imports := make(map[string]*types.Package) + for i := 0; i < 3; i++ { + if _, err := Import(imports, "./././testdata/p", "."); err != nil { + t.Fatal(err) + } + } +} diff --git a/src/go/internal/gcimporter/testdata/p.go b/src/go/internal/gcimporter/testdata/p.go new file mode 100644 index 00000000000000..9e2e7057653725 --- /dev/null +++ b/src/go/internal/gcimporter/testdata/p.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Input for TestIssue15517 + +package p + +const C = 0 + +var V int + +func F() {} diff --git a/src/go/token/position.go b/src/go/token/position.go index 33751779a3d9b5..7306083b0dd8ac 100644 --- a/src/go/token/position.go +++ b/src/go/token/position.go @@ -164,6 +164,7 @@ func (f *File) MergeLine(line int) { // Each line offset must be larger than the offset for the previous line // and smaller than the file size; otherwise SetLines fails and returns // false. +// Callers must not mutate the provided slice after SetLines returns. // func (f *File) SetLines(lines []int) bool { // verify validity of lines table diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index c7564bcf85d355..6ebf3b5eab0745 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -183,7 +183,7 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { var op operand check.expr(&op, sel.X) if op.mode == mapindex { - check.errorf(z.pos(), "cannot directly assign to struct field %s in map", ExprString(z.expr)) + check.errorf(z.pos(), "cannot assign to struct field %s in map", ExprString(z.expr)) return nil } } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index f064f6856f24f7..1ecfb35f60c3ee 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -141,6 +141,14 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { // determine type, if any if typ != nil { obj.typ = check.typ(typ) + // We cannot spread the type to all lhs variables if there + // are more than one since that would mark them as checked + // (see Checker.objDecl) and the assignment of init exprs, + // if any, would not be checked. + // + // TODO(gri) If we have no init expr, we should distribute + // a given type otherwise we need to re-evalate the type + // expr for each lhs variable, leading to duplicate work. } // check initialization @@ -173,6 +181,17 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { panic("inconsistent lhs") } } + + // We have multiple variables on the lhs and one init expr. + // Make sure all variables have been given the same type if + // one was specified, otherwise they assume the type of the + // init expression values (was issue #15755). + if typ != nil { + for _, lhs := range lhs { + lhs.typ = obj.typ + } + } + check.initVars(lhs, []ast.Expr{init}, token.NoPos) } diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 1536df5bf1bebc..992188f0ff47b5 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -67,7 +67,7 @@ func (check *Checker) arityMatch(s, init *ast.ValueSpec) { // TODO(gri) avoid declared but not used error here } else { // init exprs "inherited" - check.errorf(s.Pos(), "extra init expr at %s", init.Pos()) + check.errorf(s.Pos(), "extra init expr at %s", check.fset.Position(init.Pos())) // TODO(gri) avoid declared but not used error here } case l > r && (init != nil || r != 1): diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index e301f711590ff4..5764430b1bf337 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -123,7 +123,7 @@ func (check *Checker) multipleDefaults(list []ast.Stmt) { } if d != nil { if first != nil { - check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos()) + check.errorf(d.Pos(), "multiple defaults (first at %s)", check.fset.Position(first.Pos())) } else { first = d } diff --git a/src/go/types/testdata/issues.src b/src/go/types/testdata/issues.src index 4fe0c629386776..6579aa3b117b65 100644 --- a/src/go/types/testdata/issues.src +++ b/src/go/types/testdata/issues.src @@ -170,3 +170,19 @@ func issue14229() { _ = b % a ) } + +// Check that in a n:1 variable declaration with type and initialization +// expression the type is distributed to all variables of the lhs before +// the initialization expression assignment is checked. +func issue15755() { + // from issue + var i interface{} + type b bool + var x, y b = i.(b) + _ = x == y + + // related: we should see an error since the result of f1 is ([]int, int) + var u, v []int = f1 /* ERROR cannot use f1 */ () + _ = u + _ = v +} diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src index ac32ed7ba92f32..0c727c3dd011a9 100644 --- a/src/go/types/testdata/stmt0.src +++ b/src/go/types/testdata/stmt0.src @@ -137,7 +137,7 @@ func issue6487() { type M map[string]S var m M - m /* ERROR "cannot directly assign to struct field" */ ["foo"].x = 0 + m /* ERROR "cannot assign to struct field" */ ["foo"].x = 0 _ = &( /* ERROR "cannot take address" */ m["foo"].x) _ = &m /* ERROR "cannot take address" */ ["foo"].x } diff --git a/src/hash/crc64/crc64.go b/src/hash/crc64/crc64.go index 54cc56055e48fa..e939c2a06ac29e 100644 --- a/src/hash/crc64/crc64.go +++ b/src/hash/crc64/crc64.go @@ -24,9 +24,25 @@ const ( // Table is a 256-word table representing the polynomial for efficient processing. type Table [256]uint64 +var ( + slicing8TableISO = makeSlicingBy8Table(makeTable(ISO)) + slicing8TableECMA = makeSlicingBy8Table(makeTable(ECMA)) +) + // MakeTable returns a Table constructed from the specified polynomial. // The contents of this Table must not be modified. func MakeTable(poly uint64) *Table { + switch poly { + case ISO: + return &slicing8TableISO[0] + case ECMA: + return &slicing8TableECMA[0] + default: + return makeTable(poly) + } +} + +func makeTable(poly uint64) *Table { t := new(Table) for i := 0; i < 256; i++ { crc := uint64(i) @@ -42,6 +58,19 @@ func MakeTable(poly uint64) *Table { return t } +func makeSlicingBy8Table(t *Table) *[8]Table { + var helperTable [8]Table + helperTable[0] = *t + for i := 0; i < 256; i++ { + crc := t[i] + for j := 1; j < 8; j++ { + crc = t[crc&0xff] ^ (crc >> 8) + helperTable[j][i] = crc + } + } + return &helperTable +} + // digest represents the partial evaluation of a checksum. type digest struct { crc uint64 @@ -61,6 +90,35 @@ func (d *digest) Reset() { d.crc = 0 } func update(crc uint64, tab *Table, p []byte) uint64 { crc = ^crc + // Table comparison is somewhat expensive, so avoid it for small sizes + for len(p) >= 64 { + var helperTable *[8]Table + if *tab == slicing8TableECMA[0] { + helperTable = slicing8TableECMA + } else if *tab == slicing8TableISO[0] { + helperTable = slicing8TableISO + // For smaller sizes creating extended table takes too much time + } else if len(p) > 16384 { + helperTable = makeSlicingBy8Table(tab) + } else { + break + } + // Update using slicing-by-8 + for len(p) > 8 { + crc ^= uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | + uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 + crc = helperTable[7][crc&0xff] ^ + helperTable[6][(crc>>8)&0xff] ^ + helperTable[5][(crc>>16)&0xff] ^ + helperTable[4][(crc>>24)&0xff] ^ + helperTable[3][(crc>>32)&0xff] ^ + helperTable[2][(crc>>40)&0xff] ^ + helperTable[1][(crc>>48)&0xff] ^ + helperTable[0][crc>>56] + p = p[8:] + } + } + // For reminders or small sizes for _, v := range p { crc = tab[byte(crc)^v] ^ (crc >> 8) } diff --git a/src/hash/crc64/crc64_test.go b/src/hash/crc64/crc64_test.go index 80dca47f3d8b2c..480b150e132dcf 100644 --- a/src/hash/crc64/crc64_test.go +++ b/src/hash/crc64/crc64_test.go @@ -72,13 +72,13 @@ func TestGolden(t *testing.T) { } } -func BenchmarkISOCrc64KB(b *testing.B) { - b.SetBytes(1024) - data := make([]byte, 1024) +func bench(b *testing.B, poly uint64, size int64) { + b.SetBytes(size) + data := make([]byte, size) for i := range data { data[i] = byte(i) } - h := New(MakeTable(ISO)) + h := New(MakeTable(poly)) in := make([]byte, 0, h.Size()) b.ResetTimer() @@ -88,3 +88,24 @@ func BenchmarkISOCrc64KB(b *testing.B) { h.Sum(in) } } + +func BenchmarkCrc64(b *testing.B) { + b.Run("ISO64KB", func(b *testing.B) { + bench(b, ISO, 64<<10) + }) + b.Run("ISO4KB", func(b *testing.B) { + bench(b, ISO, 4<<10) + }) + b.Run("ISO1KB", func(b *testing.B) { + bench(b, ISO, 1<<10) + }) + b.Run("ECMA64KB", func(b *testing.B) { + bench(b, ECMA, 64<<10) + }) + b.Run("Random64KB", func(b *testing.B) { + bench(b, 0x777, 64<<10) + }) + b.Run("Random16KB", func(b *testing.B) { + bench(b, 0x777, 16<<10) + }) +} diff --git a/src/html/template/content.go b/src/html/template/content.go index 3715ed5c93805f..2e14bd1231f799 100644 --- a/src/html/template/content.go +++ b/src/html/template/content.go @@ -18,16 +18,28 @@ type ( // 4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`. // See http://www.w3.org/TR/css3-syntax/#parsing and // https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style + // + // Use of this type presents a security risk: + // the encapsulated content should come from a trusted source, + // as it will be included verbatim in the template output. CSS string // HTML encapsulates a known safe HTML document fragment. // It should not be used for HTML from a third-party, or HTML with // unclosed tags or comments. The outputs of a sound HTML sanitizer // and a template escaped by this package are fine for use with HTML. + // + // Use of this type presents a security risk: + // the encapsulated content should come from a trusted source, + // as it will be included verbatim in the template output. HTML string // HTMLAttr encapsulates an HTML attribute from a trusted source, // for example, ` dir="ltr"`. + // + // Use of this type presents a security risk: + // the encapsulated content should come from a trusted source, + // as it will be included verbatim in the template output. HTMLAttr string // JS encapsulates a known safe EcmaScript5 Expression, for example, @@ -37,6 +49,15 @@ type ( // statement/expression ambiguity as when passing an expression like // "{ foo: bar() }\n['foo']()", which is both a valid Expression and a // valid Program with a very different meaning. + // + // Use of this type presents a security risk: + // the encapsulated content should come from a trusted source, + // as it will be included verbatim in the template output. + // + // Using JS to include valid but untrusted JSON is not safe. + // A safe alternative is to parse the JSON with json.Unmarshal and then + // pass the resultant object into the template, where it will be + // converted to sanitized JSON when presented in a JavaScript context. JS string // JSStr encapsulates a sequence of characters meant to be embedded @@ -46,6 +67,10 @@ type ( // | EscapeSequence // Note that LineContinuations are not allowed. // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not. + // + // Use of this type presents a security risk: + // the encapsulated content should come from a trusted source, + // as it will be included verbatim in the template output. JSStr string // URL encapsulates a known safe URL or URL substring (see RFC 3986). @@ -53,6 +78,10 @@ type ( // from a trusted source should go in the page, but by default dynamic // `javascript:` URLs are filtered out since they are a frequently // exploited injection vector. + // + // Use of this type presents a security risk: + // the encapsulated content should come from a trusted source, + // as it will be included verbatim in the template output. URL string ) diff --git a/src/internal/nettrace/nettrace.go b/src/internal/nettrace/nettrace.go index 51a8b2cc5ae645..de3254df589f0e 100644 --- a/src/internal/nettrace/nettrace.go +++ b/src/internal/nettrace/nettrace.go @@ -32,12 +32,14 @@ type Trace struct { // actually be for circular dependency reasons. DNSDone func(netIPs []interface{}, coalesced bool, err error) - // ConnectStart is called before a Dial. In the case of - // DualStack (Happy Eyeballs) dialing, this may be called - // multiple times, from multiple goroutines. + // ConnectStart is called before a Dial, excluding Dials made + // during DNS lookups. In the case of DualStack (Happy Eyeballs) + // dialing, this may be called multiple times, from multiple + // goroutines. ConnectStart func(network, addr string) - // ConnectStart is called after a Dial with the results. It - // may also be called multiple times, like ConnectStart. + // ConnectStart is called after a Dial with the results, excluding + // Dials made during DNS lookups. It may also be called multiple + // times, like ConnectStart. ConnectDone func(network, addr string, err error) } diff --git a/src/internal/syscall/windows/registry/syscall.go b/src/internal/syscall/windows/registry/syscall.go index 02d985cec90b0f..5426cae9096330 100644 --- a/src/internal/syscall/windows/registry/syscall.go +++ b/src/internal/syscall/windows/registry/syscall.go @@ -8,7 +8,7 @@ package registry import "syscall" -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go -systemdll syscall.go +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go const ( _REG_OPTION_NON_VOLATILE = 0 diff --git a/src/internal/syscall/windows/registry/zsyscall_windows.go b/src/internal/syscall/windows/registry/zsyscall_windows.go index 7e473d4e1de7b2..62affc0b50baf4 100644 --- a/src/internal/syscall/windows/registry/zsyscall_windows.go +++ b/src/internal/syscall/windows/registry/zsyscall_windows.go @@ -2,9 +2,11 @@ package registry -import "unsafe" -import "syscall" -import "internal/syscall/windows/sysdll" +import ( + "internal/syscall/windows/sysdll" + "syscall" + "unsafe" +) var _ unsafe.Pointer diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index 2eae5e75f9e411..7b2bc79cebe4a4 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go @@ -6,7 +6,7 @@ package windows import "syscall" -//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go -systemdll syscall_windows.go +//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go const GAA_FLAG_INCLUDE_PREFIX = 0x00000010 diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index d599258976b81d..6929acfa7283f5 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -2,9 +2,11 @@ package windows -import "unsafe" -import "syscall" -import "internal/syscall/windows/sysdll" +import ( + "internal/syscall/windows/sysdll" + "syscall" + "unsafe" +) var _ unsafe.Pointer diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index 9e684e3034388e..f134f6b04a185f 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -16,6 +16,7 @@ import ( "os/exec" "path/filepath" "runtime" + "strconv" "strings" "testing" ) @@ -133,3 +134,9 @@ func SkipFlaky(t *testing.T, issue int) { t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) } } + +func SkipFlakyNet(t *testing.T) { + if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { + t.Skip("skipping test on builder known to have frequent network failures") + } +} diff --git a/src/io/example_test.go b/src/io/example_test.go index 412dfb3b921ba4..bf16de8fe2357d 100644 --- a/src/io/example_test.go +++ b/src/io/example_test.go @@ -189,7 +189,7 @@ func ExampleSectionReader_Seek() { r := strings.NewReader("some io.Reader stream to be read\n") s := io.NewSectionReader(r, 5, 16) - if _, err := s.Seek(10, 0); err != nil { + if _, err := s.Seek(10, io.SeekStart); err != nil { log.Fatal(err) } diff --git a/src/io/io.go b/src/io/io.go index c36ec2afbb0915..80398b39973918 100644 --- a/src/io/io.go +++ b/src/io/io.go @@ -274,16 +274,6 @@ type RuneScanner interface { UnreadRune() error } -// SizedReaderAt is the interface that groups the basic ReadAt method -// with a Size method that reports the total size of the underlying -// object. It represents a fixed-size data source that supports random -// access by multiple concurrent goroutines. -type SizedReaderAt interface { - ReaderAt - // Size reports the length of the data source in bytes. - Size() int64 -} - // stringWriter is the interface that wraps the WriteString method. type stringWriter interface { WriteString(s string) (n int, err error) @@ -480,11 +470,11 @@ func (s *SectionReader) Seek(offset int64, whence int) (int64, error) { switch whence { default: return 0, errWhence - case 0: + case SeekStart: offset += s.base - case 1: + case SeekCurrent: offset += s.off - case 2: + case SeekEnd: offset += s.limit } if offset < s.base { diff --git a/src/io/io_test.go b/src/io/io_test.go index e892574b0b5f2e..877e8392e279a0 100644 --- a/src/io/io_test.go +++ b/src/io/io_test.go @@ -347,7 +347,7 @@ func TestSectionReader_Seek(t *testing.T) { br := bytes.NewReader([]byte("foo")) sr := NewSectionReader(br, 0, int64(len("foo"))) - for whence := 0; whence <= 2; whence++ { + for _, whence := range []int{SeekStart, SeekCurrent, SeekEnd} { for offset := int64(-3); offset <= 4; offset++ { brOff, brErr := br.Seek(offset, whence) srOff, srErr := sr.Seek(offset, whence) @@ -359,7 +359,7 @@ func TestSectionReader_Seek(t *testing.T) { } // And verify we can just seek past the end and get an EOF - got, err := sr.Seek(100, 0) + got, err := sr.Seek(100, SeekStart) if err != nil || got != 100 { t.Errorf("Seek = %v, %v; want 100, nil", got, err) } diff --git a/src/io/multi.go b/src/io/multi.go index c23c12b151e132..ed05cac9e722d5 100644 --- a/src/io/multi.go +++ b/src/io/multi.go @@ -10,6 +10,13 @@ type multiReader struct { func (mr *multiReader) Read(p []byte) (n int, err error) { for len(mr.readers) > 0 { + // Optimization to flatten nested multiReaders (Issue 13558) + if len(mr.readers) == 1 { + if r, ok := mr.readers[0].(*multiReader); ok { + mr.readers = r.readers + continue + } + } n, err = mr.readers[0].Read(p) if n > 0 || err != EOF { if err == EOF { diff --git a/src/io/multi_test.go b/src/io/multi_test.go index 787ea341307a11..2dce36955ed260 100644 --- a/src/io/multi_test.go +++ b/src/io/multi_test.go @@ -7,9 +7,11 @@ package io_test import ( "bytes" "crypto/sha1" + "errors" "fmt" . "io" "io/ioutil" + "runtime" "strings" "testing" ) @@ -164,3 +166,33 @@ func TestMultiWriterCopy(t *testing.T) { t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world") } } + +// readerFunc is an io.Reader implemented by the underlying func. +type readerFunc func(p []byte) (int, error) + +func (f readerFunc) Read(p []byte) (int, error) { + return f(p) +} + +// Test that MultiReader properly flattens chained multiReaders when Read is called +func TestMultiReaderFlatten(t *testing.T) { + pc := make([]uintptr, 1000) // 1000 should fit the full stack + var myDepth = runtime.Callers(0, pc) + var readDepth int // will contain the depth from which fakeReader.Read was called + var r Reader = MultiReader(readerFunc(func(p []byte) (int, error) { + readDepth = runtime.Callers(1, pc) + return 0, errors.New("irrelevant") + })) + + // chain a bunch of multiReaders + for i := 0; i < 100; i++ { + r = MultiReader(r) + } + + r.Read(nil) // don't care about errors, just want to check the call-depth for Read + + if readDepth != myDepth+2 { // 2 should be multiReader.Read and fakeReader.Read + t.Errorf("multiReader did not flatten chained multiReaders: expected readDepth %d, got %d", + myDepth+2, readDepth) + } +} diff --git a/src/make.bash b/src/make.bash index 82c903eadbb8b6..1a1412a00cf57e 100755 --- a/src/make.bash +++ b/src/make.bash @@ -50,6 +50,9 @@ # GO_DISTFLAGS: extra flags to provide to "dist bootstrap". set -e + +unset GOBIN # Issue 14340 + if [ ! -f run.bash ]; then echo 'make.bash must be run from $GOROOT/src' 1>&2 exit 1 diff --git a/src/make.bat b/src/make.bat index a64777ee917d31..bf25b95ca5097d 100644 --- a/src/make.bat +++ b/src/make.bat @@ -68,6 +68,7 @@ setlocal set GOROOT=%GOROOT_BOOTSTRAP% set GOOS= set GOARCH= +set GOBIN= "%GOROOT_BOOTSTRAP%\bin\go" build -o cmd\dist\dist.exe .\cmd\dist endlocal if errorlevel 1 goto fail diff --git a/src/make.rc b/src/make.rc index 60162045ede4b0..243f83cc0f0223 100755 --- a/src/make.rc +++ b/src/make.rc @@ -80,7 +80,7 @@ if(~ $sysname vx32) if(! ~ $GOHOSTARCH $GOARCH || ! ~ $GOHOSTOS $GOOS){ echo '##### Building packages and commands for host,' $GOHOSTOS/$GOHOSTARCH^. - GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \ + GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH GOBIN= \ $GOTOOLDIR/go_bootstrap install -gcflags $"GO_GCFLAGS -ldflags $"GO_LDFLAGS -v $pflag std cmd echo } diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go index 7d2f69a7511572..75862b4951fffe 100644 --- a/src/math/big/arith_test.go +++ b/src/math/big/arith_test.go @@ -5,6 +5,7 @@ package big import ( + "fmt" "math/rand" "testing" ) @@ -118,28 +119,22 @@ func rndV(n int) []Word { return v } -func benchmarkFunVV(b *testing.B, f funVV, n int) { - x := rndV(n) - y := rndV(n) - z := make([]Word, n) - b.SetBytes(int64(n * _W)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - f(z, x, y) +var benchSizes = []int{1, 2, 3, 4, 5, 1e1, 1e2, 1e3, 1e4, 1e5} + +func BenchmarkAddVV(b *testing.B) { + for _, n := range benchSizes { + x := rndV(n) + y := rndV(n) + z := make([]Word, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n * _W)) + for i := 0; i < b.N; i++ { + addVV(z, x, y) + } + }) } } -func BenchmarkAddVV_1(b *testing.B) { benchmarkFunVV(b, addVV, 1) } -func BenchmarkAddVV_2(b *testing.B) { benchmarkFunVV(b, addVV, 2) } -func BenchmarkAddVV_3(b *testing.B) { benchmarkFunVV(b, addVV, 3) } -func BenchmarkAddVV_4(b *testing.B) { benchmarkFunVV(b, addVV, 4) } -func BenchmarkAddVV_5(b *testing.B) { benchmarkFunVV(b, addVV, 5) } -func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) } -func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) } -func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) } -func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) } -func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) } - type funVW func(z, x []Word, y Word) (c Word) type argVW struct { z, x nat @@ -236,28 +231,20 @@ func TestFunVW(t *testing.T) { } } -func benchmarkFunVW(b *testing.B, f funVW, n int) { - x := rndV(n) - y := rndW() - z := make([]Word, n) - b.SetBytes(int64(n * _S)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - f(z, x, y) +func BenchmarkAddVW(b *testing.B) { + for _, n := range benchSizes { + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n * _S)) + for i := 0; i < b.N; i++ { + addVW(z, x, y) + } + }) } } -func BenchmarkAddVW_1(b *testing.B) { benchmarkFunVW(b, addVW, 1) } -func BenchmarkAddVW_2(b *testing.B) { benchmarkFunVW(b, addVW, 2) } -func BenchmarkAddVW_3(b *testing.B) { benchmarkFunVW(b, addVW, 3) } -func BenchmarkAddVW_4(b *testing.B) { benchmarkFunVW(b, addVW, 4) } -func BenchmarkAddVW_5(b *testing.B) { benchmarkFunVW(b, addVW, 5) } -func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) } -func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) } -func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) } -func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) } -func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) } - type funVWW func(z, x []Word, y, r Word) (c Word) type argVWW struct { z, x nat @@ -382,28 +369,20 @@ func TestMulAddWWW(t *testing.T) { } } -func benchmarkAddMulVVW(b *testing.B, n int) { - x := rndV(n) - y := rndW() - z := make([]Word, n) - b.SetBytes(int64(n * _W)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - addMulVVW(z, x, y) +func BenchmarkAddMulVVW(b *testing.B) { + for _, n := range benchSizes { + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n * _W)) + for i := 0; i < b.N; i++ { + addMulVVW(z, x, y) + } + }) } } -func BenchmarkAddMulVVW_1(b *testing.B) { benchmarkAddMulVVW(b, 1) } -func BenchmarkAddMulVVW_2(b *testing.B) { benchmarkAddMulVVW(b, 2) } -func BenchmarkAddMulVVW_3(b *testing.B) { benchmarkAddMulVVW(b, 3) } -func BenchmarkAddMulVVW_4(b *testing.B) { benchmarkAddMulVVW(b, 4) } -func BenchmarkAddMulVVW_5(b *testing.B) { benchmarkAddMulVVW(b, 5) } -func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) } -func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) } -func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) } -func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) } -func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) } - func testWordBitLen(t *testing.T, fname string, f func(Word) int) { for i := 0; i <= _W; i++ { x := Word(1) << uint(i-1) // i == 0 => x == 0 @@ -420,23 +399,15 @@ func TestWordBitLen(t *testing.T) { } // runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1. -func benchmarkBitLenN(b *testing.B, nbits uint) { - testword := Word((uint64(1) << nbits) - 1) - for i := 0; i < b.N; i++ { - bitLen(testword) +func BenchmarkBitLen(b *testing.B) { + // Individual bitLen tests. Numbers chosen to examine both sides + // of powers-of-two boundaries. + for _, nbits := range []uint{0, 1, 2, 3, 4, 5, 8, 9, 16, 17, 31} { + testword := Word((uint64(1) << nbits) - 1) + b.Run(fmt.Sprint(nbits), func(b *testing.B) { + for i := 0; i < b.N; i++ { + bitLen(testword) + } + }) } } - -// Individual bitLen tests. Numbers chosen to examine both sides -// of powers-of-two boundaries. -func BenchmarkBitLen0(b *testing.B) { benchmarkBitLenN(b, 0) } -func BenchmarkBitLen1(b *testing.B) { benchmarkBitLenN(b, 1) } -func BenchmarkBitLen2(b *testing.B) { benchmarkBitLenN(b, 2) } -func BenchmarkBitLen3(b *testing.B) { benchmarkBitLenN(b, 3) } -func BenchmarkBitLen4(b *testing.B) { benchmarkBitLenN(b, 4) } -func BenchmarkBitLen5(b *testing.B) { benchmarkBitLenN(b, 5) } -func BenchmarkBitLen8(b *testing.B) { benchmarkBitLenN(b, 8) } -func BenchmarkBitLen9(b *testing.B) { benchmarkBitLenN(b, 9) } -func BenchmarkBitLen16(b *testing.B) { benchmarkBitLenN(b, 16) } -func BenchmarkBitLen17(b *testing.B) { benchmarkBitLenN(b, 17) } -func BenchmarkBitLen31(b *testing.B) { benchmarkBitLenN(b, 31) } diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go index 563ccb30523ada..ebb29856549b8e 100644 --- a/src/math/big/nat_test.go +++ b/src/math/big/nat_test.go @@ -5,6 +5,7 @@ package big import ( + "fmt" "runtime" "strings" "testing" @@ -509,24 +510,20 @@ func TestExpNN(t *testing.T) { } } -func ExpHelper(b *testing.B, x, y Word) { - var z nat - for i := 0; i < b.N; i++ { - z.expWW(x, y) +func BenchmarkExp3Power(b *testing.B) { + const x = 3 + for _, y := range []Word{ + 0x10, 0x40, 0x100, 0x400, 0x1000, 0x4000, 0x10000, 0x40000, 0x100000, 0x400000, + } { + b.Run(fmt.Sprintf("%#x", y), func(b *testing.B) { + var z nat + for i := 0; i < b.N; i++ { + z.expWW(x, y) + } + }) } } -func BenchmarkExp3Power0x10(b *testing.B) { ExpHelper(b, 3, 0x10) } -func BenchmarkExp3Power0x40(b *testing.B) { ExpHelper(b, 3, 0x40) } -func BenchmarkExp3Power0x100(b *testing.B) { ExpHelper(b, 3, 0x100) } -func BenchmarkExp3Power0x400(b *testing.B) { ExpHelper(b, 3, 0x400) } -func BenchmarkExp3Power0x1000(b *testing.B) { ExpHelper(b, 3, 0x1000) } -func BenchmarkExp3Power0x4000(b *testing.B) { ExpHelper(b, 3, 0x4000) } -func BenchmarkExp3Power0x10000(b *testing.B) { ExpHelper(b, 3, 0x10000) } -func BenchmarkExp3Power0x40000(b *testing.B) { ExpHelper(b, 3, 0x40000) } -func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) } -func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) } - func fibo(n int) nat { switch n { case 0: diff --git a/src/math/big/natconv.go b/src/math/big/natconv.go index e216bd288ccd2c..44547842c1ced2 100644 --- a/src/math/big/natconv.go +++ b/src/math/big/natconv.go @@ -391,7 +391,7 @@ func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []diviso // this appears to be faster for BenchmarkString10000Base10 // and smaller strings (but a bit slower for larger ones) t := r / 10 - s[i] = '0' + byte(r-t<<3-t-t) // TODO(gri) replace w/ t*10 once compiler produces better code + s[i] = '0' + byte(r-t*10) r = t } } diff --git a/src/math/big/natconv_test.go b/src/math/big/natconv_test.go index 028e5a858eb98c..79901d1880477f 100644 --- a/src/math/big/natconv_test.go +++ b/src/math/big/natconv_test.go @@ -6,6 +6,7 @@ package big import ( "bytes" + "fmt" "io" "strings" "testing" @@ -273,102 +274,58 @@ func BenchmarkStringPiParallel(b *testing.B) { }) } -func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) } -func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) } -func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) } -func BenchmarkScan10000Base2(b *testing.B) { ScanHelper(b, 2, 10, 10000) } -func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) } - -func BenchmarkScan10Base8(b *testing.B) { ScanHelper(b, 8, 10, 10) } -func BenchmarkScan100Base8(b *testing.B) { ScanHelper(b, 8, 10, 100) } -func BenchmarkScan1000Base8(b *testing.B) { ScanHelper(b, 8, 10, 1000) } -func BenchmarkScan10000Base8(b *testing.B) { ScanHelper(b, 8, 10, 10000) } -func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) } - -func BenchmarkScan10Base10(b *testing.B) { ScanHelper(b, 10, 10, 10) } -func BenchmarkScan100Base10(b *testing.B) { ScanHelper(b, 10, 10, 100) } -func BenchmarkScan1000Base10(b *testing.B) { ScanHelper(b, 10, 10, 1000) } -func BenchmarkScan10000Base10(b *testing.B) { ScanHelper(b, 10, 10, 10000) } -func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) } - -func BenchmarkScan10Base16(b *testing.B) { ScanHelper(b, 16, 10, 10) } -func BenchmarkScan100Base16(b *testing.B) { ScanHelper(b, 16, 10, 100) } -func BenchmarkScan1000Base16(b *testing.B) { ScanHelper(b, 16, 10, 1000) } -func BenchmarkScan10000Base16(b *testing.B) { ScanHelper(b, 16, 10, 10000) } -func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) } - -func ScanHelper(b *testing.B, base int, x, y Word) { - b.StopTimer() - var z nat - z = z.expWW(x, y) - - s := z.utoa(base) - if t := itoa(z, base); !bytes.Equal(s, t) { - b.Fatalf("scanning: got %s; want %s", s, t) +func BenchmarkScan(b *testing.B) { + const x = 10 + for _, base := range []int{2, 8, 10, 16} { + for _, y := range []Word{10, 100, 1000, 10000, 100000} { + b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) { + b.StopTimer() + var z nat + z = z.expWW(x, y) + + s := z.utoa(base) + if t := itoa(z, base); !bytes.Equal(s, t) { + b.Fatalf("scanning: got %s; want %s", s, t) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + z.scan(bytes.NewReader(s), base, false) + } + }) + } } - b.StartTimer() +} - for i := 0; i < b.N; i++ { - z.scan(bytes.NewReader(s), base, false) +func BenchmarkString(b *testing.B) { + const x = 10 + for _, base := range []int{2, 8, 10, 16} { + for _, y := range []Word{10, 100, 1000, 10000, 100000} { + b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) { + b.StopTimer() + var z nat + z = z.expWW(x, y) + z.utoa(base) // warm divisor cache + b.StartTimer() + + for i := 0; i < b.N; i++ { + _ = z.utoa(base) + } + }) + } } } -func BenchmarkString10Base2(b *testing.B) { StringHelper(b, 2, 10, 10) } -func BenchmarkString100Base2(b *testing.B) { StringHelper(b, 2, 10, 100) } -func BenchmarkString1000Base2(b *testing.B) { StringHelper(b, 2, 10, 1000) } -func BenchmarkString10000Base2(b *testing.B) { StringHelper(b, 2, 10, 10000) } -func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) } - -func BenchmarkString10Base8(b *testing.B) { StringHelper(b, 8, 10, 10) } -func BenchmarkString100Base8(b *testing.B) { StringHelper(b, 8, 10, 100) } -func BenchmarkString1000Base8(b *testing.B) { StringHelper(b, 8, 10, 1000) } -func BenchmarkString10000Base8(b *testing.B) { StringHelper(b, 8, 10, 10000) } -func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) } - -func BenchmarkString10Base10(b *testing.B) { StringHelper(b, 10, 10, 10) } -func BenchmarkString100Base10(b *testing.B) { StringHelper(b, 10, 10, 100) } -func BenchmarkString1000Base10(b *testing.B) { StringHelper(b, 10, 10, 1000) } -func BenchmarkString10000Base10(b *testing.B) { StringHelper(b, 10, 10, 10000) } -func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) } - -func BenchmarkString10Base16(b *testing.B) { StringHelper(b, 16, 10, 10) } -func BenchmarkString100Base16(b *testing.B) { StringHelper(b, 16, 10, 100) } -func BenchmarkString1000Base16(b *testing.B) { StringHelper(b, 16, 10, 1000) } -func BenchmarkString10000Base16(b *testing.B) { StringHelper(b, 16, 10, 10000) } -func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) } - -func StringHelper(b *testing.B, base int, x, y Word) { - b.StopTimer() - var z nat - z = z.expWW(x, y) - z.utoa(base) // warm divisor cache - b.StartTimer() - - for i := 0; i < b.N; i++ { - _ = z.utoa(base) +func BenchmarkLeafSize(b *testing.B) { + for n := 0; n <= 16; n++ { + b.Run(fmt.Sprint(n), func(b *testing.B) { LeafSizeHelper(b, 10, n) }) + } + // Try some large lengths + for _, n := range []int{32, 64} { + b.Run(fmt.Sprint(n), func(b *testing.B) { LeafSizeHelper(b, 10, n) }) } } -func BenchmarkLeafSize0(b *testing.B) { LeafSizeHelper(b, 10, 0) } // test without splitting -func BenchmarkLeafSize1(b *testing.B) { LeafSizeHelper(b, 10, 1) } -func BenchmarkLeafSize2(b *testing.B) { LeafSizeHelper(b, 10, 2) } -func BenchmarkLeafSize3(b *testing.B) { LeafSizeHelper(b, 10, 3) } -func BenchmarkLeafSize4(b *testing.B) { LeafSizeHelper(b, 10, 4) } -func BenchmarkLeafSize5(b *testing.B) { LeafSizeHelper(b, 10, 5) } -func BenchmarkLeafSize6(b *testing.B) { LeafSizeHelper(b, 10, 6) } -func BenchmarkLeafSize7(b *testing.B) { LeafSizeHelper(b, 10, 7) } -func BenchmarkLeafSize8(b *testing.B) { LeafSizeHelper(b, 10, 8) } -func BenchmarkLeafSize9(b *testing.B) { LeafSizeHelper(b, 10, 9) } -func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) } -func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) } -func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) } -func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) } -func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) } -func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) } -func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) } -func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths -func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } - func LeafSizeHelper(b *testing.B, base, size int) { b.StopTimer() originalLeafSize := leafSize diff --git a/src/math/rand/rand.go b/src/math/rand/rand.go index d693bfb52f243c..add039ed4bdade 100644 --- a/src/math/rand/rand.go +++ b/src/math/rand/rand.go @@ -179,7 +179,8 @@ var globalRand = New(&lockedSource{src: NewSource(1)}) // Seed uses the provided seed value to initialize the default Source to a // deterministic state. If Seed is not called, the generator behaves as -// if seeded by Seed(1). +// if seeded by Seed(1). Only uses the bottom 31 bits of seed; the top 33 +// bits are ignored. func Seed(seed int64) { globalRand.Seed(seed) } // Int63 returns a non-negative pseudo-random 63-bit integer as an int64 diff --git a/src/mime/multipart/writer.go b/src/mime/multipart/writer.go index 80960939d62ecb..f82756d55185e2 100644 --- a/src/mime/multipart/writer.go +++ b/src/mime/multipart/writer.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "net/textproto" + "sort" "strings" ) @@ -94,10 +95,14 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) { } else { fmt.Fprintf(&b, "--%s\r\n", w.boundary) } - // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort - // and clean, like http.Header.Write(w) does. - for k, vv := range header { - for _, v := range vv { + + keys := make([]string, 0, len(header)) + for k := range header { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + for _, v := range header[k] { fmt.Fprintf(&b, "%s: %s\r\n", k, v) } } diff --git a/src/mime/multipart/writer_test.go b/src/mime/multipart/writer_test.go index ba00c97eceea4b..9670c660a4a0b7 100644 --- a/src/mime/multipart/writer_test.go +++ b/src/mime/multipart/writer_test.go @@ -7,6 +7,7 @@ package multipart import ( "bytes" "io/ioutil" + "net/textproto" "strings" "testing" ) @@ -126,3 +127,32 @@ func TestWriterBoundaryGoroutines(t *testing.T) { w.Boundary() <-done } + +func TestSortedHeader(t *testing.T) { + var buf bytes.Buffer + w := NewWriter(&buf) + if err := w.SetBoundary("MIMEBOUNDARY"); err != nil { + t.Fatalf("Error setting mime boundary: %v", err) + } + + header := textproto.MIMEHeader{ + "A": {"2"}, + "B": {"5", "7", "6"}, + "C": {"4"}, + "M": {"3"}, + "Z": {"1"}, + } + + part, err := w.CreatePart(header) + if err != nil { + t.Fatalf("Unable to create part: %v", err) + } + part.Write([]byte("foo")) + + w.Close() + + want := "--MIMEBOUNDARY\r\nA: 2\r\nB: 5\r\nB: 7\r\nB: 6\r\nC: 4\r\nM: 3\r\nZ: 1\r\n\r\nfoo\r\n--MIMEBOUNDARY--\r\n" + if want != buf.String() { + t.Fatalf("\n got: %q\nwant: %q\n", buf.String(), want) + } +} diff --git a/src/mime/type_plan9.go b/src/mime/type_plan9.go index c3ba186e7c6b48..14ff9734051863 100644 --- a/src/mime/type_plan9.go +++ b/src/mime/type_plan9.go @@ -21,7 +21,7 @@ func initMimePlan9() { } var typeFiles = []string{ - "/sys/lib/mimetypes", + "/sys/lib/mimetype", } func initMimeForTests() map[string]string { diff --git a/src/net/cgo_stub.go b/src/net/cgo_stub.go index 52d1dfd3460e6c..51259722aec8e6 100644 --- a/src/net/cgo_stub.go +++ b/src/net/cgo_stub.go @@ -6,6 +6,8 @@ package net +import "context" + func init() { netGo = true } type addrinfoErrno int @@ -14,22 +16,22 @@ func (eai addrinfoErrno) Error() string { return "" } func (eai addrinfoErrno) Temporary() bool { return false } func (eai addrinfoErrno) Timeout() bool { return false } -func cgoLookupHost(name string) (addrs []string, err error, completed bool) { +func cgoLookupHost(ctx context.Context, name string) (addrs []string, err error, completed bool) { return nil, nil, false } -func cgoLookupPort(network, service string) (port int, err error, completed bool) { +func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) { return 0, nil, false } -func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) { +func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) { return nil, nil, false } -func cgoLookupCNAME(name string) (cname string, err error, completed bool) { +func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) { return "", nil, false } -func cgoLookupPTR(addr string) (ptrs []string, err error, completed bool) { +func cgoLookupPTR(ctx context.Context, addr string) (ptrs []string, err error, completed bool) { return nil, nil, false } diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go index 59c40c8d8a7f0c..5a1eed843751a4 100644 --- a/src/net/cgo_unix.go +++ b/src/net/cgo_unix.go @@ -19,6 +19,7 @@ package net import "C" import ( + "context" "syscall" "unsafe" ) @@ -32,18 +33,31 @@ func (eai addrinfoErrno) Error() string { return C.GoString(C.gai_strerror(C.i func (eai addrinfoErrno) Temporary() bool { return eai == C.EAI_AGAIN } func (eai addrinfoErrno) Timeout() bool { return false } -func cgoLookupHost(name string) (hosts []string, err error, completed bool) { - addrs, err, completed := cgoLookupIP(name) +type portLookupResult struct { + port int + err error +} + +type ipLookupResult struct { + addrs []IPAddr + cname string + err error +} + +type reverseLookupResult struct { + names []string + err error +} + +func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) { + addrs, err, completed := cgoLookupIP(ctx, name) for _, addr := range addrs { hosts = append(hosts, addr.String()) } return } -func cgoLookupPort(network, service string) (port int, err error, completed bool) { - acquireThread() - defer releaseThread() - +func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) { var hints C.struct_addrinfo switch network { case "": // no hints @@ -64,11 +78,27 @@ func cgoLookupPort(network, service string) (port int, err error, completed bool hints.ai_family = C.AF_INET6 } } + if ctx.Done() == nil { + port, err := cgoLookupServicePort(&hints, network, service) + return port, err, true + } + result := make(chan portLookupResult, 1) + go cgoPortLookup(result, &hints, network, service) + select { + case r := <-result: + return r.port, r.err, true + case <-ctx.Done(): + // Since there isn't a portable way to cancel the lookup, + // we just let it finish and write to the buffered channel. + return 0, mapErr(ctx.Err()), false + } +} +func cgoLookupServicePort(hints *C.struct_addrinfo, network, service string) (port int, err error) { s := C.CString(service) var res *C.struct_addrinfo defer C.free(unsafe.Pointer(s)) - gerrno, err := C.getaddrinfo(nil, s, &hints, &res) + gerrno, err := C.getaddrinfo(nil, s, hints, &res) if gerrno != 0 { switch gerrno { case C.EAI_SYSTEM: @@ -78,7 +108,7 @@ func cgoLookupPort(network, service string) (port int, err error, completed bool default: err = addrinfoErrno(gerrno) } - return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}, true + return 0, &DNSError{Err: err.Error(), Name: network + "/" + service} } defer C.freeaddrinfo(res) @@ -87,17 +117,22 @@ func cgoLookupPort(network, service string) (port int, err error, completed bool case C.AF_INET: sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) p := (*[2]byte)(unsafe.Pointer(&sa.Port)) - return int(p[0])<<8 | int(p[1]), nil, true + return int(p[0])<<8 | int(p[1]), nil case C.AF_INET6: sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) p := (*[2]byte)(unsafe.Pointer(&sa.Port)) - return int(p[0])<<8 | int(p[1]), nil, true + return int(p[0])<<8 | int(p[1]), nil } } - return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}, true + return 0, &DNSError{Err: "unknown port", Name: network + "/" + service} } -func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) { +func cgoPortLookup(result chan<- portLookupResult, hints *C.struct_addrinfo, network, service string) { + port, err := cgoLookupServicePort(hints, network, service) + result <- portLookupResult{port, err} +} + +func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) { acquireThread() defer releaseThread() @@ -127,7 +162,7 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, com default: err = addrinfoErrno(gerrno) } - return nil, "", &DNSError{Err: err.Error(), Name: name}, true + return nil, "", &DNSError{Err: err.Error(), Name: name} } defer C.freeaddrinfo(res) @@ -156,17 +191,42 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, com addrs = append(addrs, addr) } } - return addrs, cname, nil, true + return addrs, cname, nil } -func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) { - addrs, _, err, completed = cgoLookupIPCNAME(name) - return +func cgoIPLookup(result chan<- ipLookupResult, name string) { + addrs, cname, err := cgoLookupIPCNAME(name) + result <- ipLookupResult{addrs, cname, err} } -func cgoLookupCNAME(name string) (cname string, err error, completed bool) { - _, cname, err, completed = cgoLookupIPCNAME(name) - return +func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) { + if ctx.Done() == nil { + addrs, _, err = cgoLookupIPCNAME(name) + return addrs, err, true + } + result := make(chan ipLookupResult, 1) + go cgoIPLookup(result, name) + select { + case r := <-result: + return r.addrs, r.err, true + case <-ctx.Done(): + return nil, mapErr(ctx.Err()), false + } +} + +func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) { + if ctx.Done() == nil { + _, cname, err = cgoLookupIPCNAME(name) + return cname, err, true + } + result := make(chan ipLookupResult, 1) + go cgoIPLookup(result, name) + select { + case r := <-result: + return r.cname, r.err, true + case <-ctx.Done(): + return "", mapErr(ctx.Err()), false + } } // These are roughly enough for the following: @@ -182,10 +242,7 @@ const ( maxNameinfoLen = 4096 ) -func cgoLookupPTR(addr string) ([]string, error, bool) { - acquireThread() - defer releaseThread() - +func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) { var zone string ip := parseIPv4(addr) if ip == nil { @@ -198,9 +255,26 @@ func cgoLookupPTR(addr string) ([]string, error, bool) { if sa == nil { return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true } - var err error - var b []byte + if ctx.Done() == nil { + names, err := cgoLookupAddrPTR(addr, sa, salen) + return names, err, true + } + result := make(chan reverseLookupResult, 1) + go cgoReverseLookup(result, addr, sa, salen) + select { + case r := <-result: + return r.names, r.err, true + case <-ctx.Done(): + return nil, mapErr(ctx.Err()), false + } +} + +func cgoLookupAddrPTR(addr string, sa *C.struct_sockaddr, salen C.socklen_t) (names []string, err error) { + acquireThread() + defer releaseThread() + var gerrno int + var b []byte for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 { b = make([]byte, l) gerrno, err = cgoNameinfoPTR(b, sa, salen) @@ -217,16 +291,20 @@ func cgoLookupPTR(addr string) ([]string, error, bool) { default: err = addrinfoErrno(gerrno) } - return nil, &DNSError{Err: err.Error(), Name: addr}, true + return nil, &DNSError{Err: err.Error(), Name: addr} } - for i := 0; i < len(b); i++ { if b[i] == 0 { b = b[:i] break } } - return []string{absDomainName(b)}, nil, true + return []string{absDomainName(b)}, nil +} + +func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *C.struct_sockaddr, salen C.socklen_t) { + names, err := cgoLookupAddrPTR(addr, sa, salen) + result <- reverseLookupResult{names, err} } func cgoSockaddr(ip IP, zone string) (*C.struct_sockaddr, C.socklen_t) { diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go index 5dc7b1a62d4663..e861c7aa1f90d8 100644 --- a/src/net/cgo_unix_test.go +++ b/src/net/cgo_unix_test.go @@ -13,15 +13,70 @@ import ( ) func TestCgoLookupIP(t *testing.T) { - host := "localhost" - _, err, ok := cgoLookupIP(host) + ctx := context.Background() + _, err, ok := cgoLookupIP(ctx, "localhost") if !ok { t.Errorf("cgoLookupIP must not be a placeholder") } if err != nil { t.Error(err) } - if _, err := goLookupIP(context.Background(), host); err != nil { +} + +func TestCgoLookupIPWithCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, err, ok := cgoLookupIP(ctx, "localhost") + if !ok { + t.Errorf("cgoLookupIP must not be a placeholder") + } + if err != nil { + t.Error(err) + } +} + +func TestCgoLookupPort(t *testing.T) { + ctx := context.Background() + _, err, ok := cgoLookupPort(ctx, "tcp", "smtp") + if !ok { + t.Errorf("cgoLookupPort must not be a placeholder") + } + if err != nil { + t.Error(err) + } +} + +func TestCgoLookupPortWithCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, err, ok := cgoLookupPort(ctx, "tcp", "smtp") + if !ok { + t.Errorf("cgoLookupPort must not be a placeholder") + } + if err != nil { + t.Error(err) + } +} + +func TestCgoLookupPTR(t *testing.T) { + ctx := context.Background() + _, err, ok := cgoLookupPTR(ctx, "127.0.0.1") + if !ok { + t.Errorf("cgoLookupPTR must not be a placeholder") + } + if err != nil { + t.Error(err) + } +} + +func TestCgoLookupPTRWithCancel(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, err, ok := cgoLookupPTR(ctx, "127.0.0.1") + if !ok { + t.Errorf("cgoLookupPTR must not be a placeholder") + } + if err != nil { t.Error(err) } } diff --git a/src/net/conn_test.go b/src/net/conn_test.go index 8accbae7bb8c47..16cf69ee169eaf 100644 --- a/src/net/conn_test.go +++ b/src/net/conn_test.go @@ -43,7 +43,7 @@ func TestConnAndListener(t *testing.T) { t.Fatal(err) } defer c.Close() - if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network { + if c.LocalAddr().Network() != network || c.RemoteAddr().Network() != network { t.Fatalf("got %s->%s; want %s->%s", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network) } c.SetDeadline(time.Now().Add(someTimeout)) diff --git a/src/net/dial.go b/src/net/dial.go index 256ef3806147c4..55edb433953ae8 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -241,8 +241,8 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) ( // If the host is empty, as in ":80", the local system is assumed. // // Examples: -// Dial("tcp", "12.34.56.78:80") -// Dial("tcp", "google.com:http") +// Dial("tcp", "192.0.2.1:80") +// Dial("tcp", "golang.org:http") // Dial("tcp", "[2001:db8::1]:http") // Dial("tcp", "[fe80::1%lo0]:80") // Dial("tcp", ":80") @@ -252,8 +252,8 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) ( // literal IP address. // // Examples: -// Dial("ip4:1", "127.0.0.1") -// Dial("ip6:ospf", "::1") +// Dial("ip4:1", "192.0.2.1") +// Dial("ip6:ipv6-icmp", "2001:db8::1") // // For Unix networks, the address must be a file system path. func Dial(network, address string) (Conn, error) { @@ -317,7 +317,16 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn ctx = subCtx } - addrs, err := resolveAddrList(ctx, "dial", network, address, d.LocalAddr) + // Shadow the nettrace (if any) during resolve so Connect events don't fire for DNS lookups. + resolveCtx := ctx + if trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace); trace != nil { + shadow := *trace + shadow.ConnectStart = nil + shadow.ConnectDone = nil + resolveCtx = context.WithValue(resolveCtx, nettrace.TraceKey{}, &shadow) + } + + addrs, err := resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr) if err != nil { return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err} } diff --git a/src/net/dial_test.go b/src/net/dial_test.go index ead1e68d46f43e..53656770112a3a 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -128,13 +128,16 @@ func TestDialerDualStackFDLeak(t *testing.T) { t.Skipf("%s does not have full support of socktest", runtime.GOOS) case "windows": t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS) - case "openbsd": - testenv.SkipFlaky(t, 15157) } if !supportsIPv4 || !supportsIPv6 { t.Skip("both IPv4 and IPv6 are required") } + closedPortDelay, expectClosedPortDelay := dialClosedPort() + if closedPortDelay > expectClosedPortDelay { + t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay) + } + before := sw.Sockets() origTestHookLookupIP := testHookLookupIP defer func() { testHookLookupIP = origTestHookLookupIP }() @@ -148,10 +151,7 @@ func TestDialerDualStackFDLeak(t *testing.T) { c.Close() } } - dss, err := newDualStackServer([]streamListener{ - {network: "tcp4", address: "127.0.0.1"}, - {network: "tcp6", address: "::1"}, - }) + dss, err := newDualStackServer() if err != nil { t.Fatal(err) } @@ -163,7 +163,7 @@ func TestDialerDualStackFDLeak(t *testing.T) { const N = 10 var wg sync.WaitGroup wg.Add(N) - d := &Dialer{DualStack: true, Timeout: 100 * time.Millisecond} + d := &Dialer{DualStack: true, Timeout: 100*time.Millisecond + closedPortDelay} for i := 0; i < N; i++ { go func() { defer wg.Done() @@ -326,10 +326,7 @@ func TestDialParallel(t *testing.T) { } for i, tt := range testCases { - dss, err := newDualStackServer([]streamListener{ - {network: "tcp4", address: "127.0.0.1"}, - {network: "tcp6", address: "::1"}, - }) + dss, err := newDualStackServer() if err != nil { t.Fatal(err) } @@ -446,9 +443,7 @@ func TestDialerFallbackDelay(t *testing.T) { c.Close() } } - dss, err := newDualStackServer([]streamListener{ - {network: "tcp", address: "127.0.0.1"}, - }) + dss, err := newDualStackServer() if err != nil { t.Fatal(err) } @@ -501,10 +496,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) { c.Close() wg.Done() } - dss, err := newDualStackServer([]streamListener{ - {network: "tcp4", address: "127.0.0.1"}, - {network: "tcp6", address: "::1"}, - }) + dss, err := newDualStackServer() if err != nil { t.Fatal(err) } @@ -591,68 +583,67 @@ func TestDialerPartialDeadline(t *testing.T) { } } -type dialerLocalAddrTest struct { - network, raddr string - laddr Addr - error -} - -var dialerLocalAddrTests = []dialerLocalAddrTest{ - {"tcp4", "127.0.0.1", nil, nil}, - {"tcp4", "127.0.0.1", &TCPAddr{}, nil}, - {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, - {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, - {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"}}, - {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil}, - {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil}, - {"tcp4", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress}, - {"tcp4", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}}, - {"tcp4", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}}, - - {"tcp6", "::1", nil, nil}, - {"tcp6", "::1", &TCPAddr{}, nil}, - {"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, - {"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, - {"tcp6", "::1", &TCPAddr{IP: ParseIP("::")}, nil}, - {"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress}, - {"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress}, - {"tcp6", "::1", &TCPAddr{IP: IPv6loopback}, nil}, - {"tcp6", "::1", &UDPAddr{}, &AddrError{Err: "some error"}}, - {"tcp6", "::1", &UnixAddr{}, &AddrError{Err: "some error"}}, - - {"tcp", "127.0.0.1", nil, nil}, - {"tcp", "127.0.0.1", &TCPAddr{}, nil}, - {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, - {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, - {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil}, - {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil}, - {"tcp", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress}, - {"tcp", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}}, - {"tcp", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}}, - - {"tcp", "::1", nil, nil}, - {"tcp", "::1", &TCPAddr{}, nil}, - {"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, - {"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, - {"tcp", "::1", &TCPAddr{IP: ParseIP("::")}, nil}, - {"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress}, - {"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress}, - {"tcp", "::1", &TCPAddr{IP: IPv6loopback}, nil}, - {"tcp", "::1", &UDPAddr{}, &AddrError{Err: "some error"}}, - {"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}}, -} - func TestDialerLocalAddr(t *testing.T) { if !supportsIPv4 || !supportsIPv6 { t.Skip("both IPv4 and IPv6 are required") } + type test struct { + network, raddr string + laddr Addr + error + } + var tests = []test{ + {"tcp4", "127.0.0.1", nil, nil}, + {"tcp4", "127.0.0.1", &TCPAddr{}, nil}, + {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, + {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, + {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"}}, + {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil}, + {"tcp4", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil}, + {"tcp4", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress}, + {"tcp4", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}}, + {"tcp4", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}}, + + {"tcp6", "::1", nil, nil}, + {"tcp6", "::1", &TCPAddr{}, nil}, + {"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, + {"tcp6", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, + {"tcp6", "::1", &TCPAddr{IP: ParseIP("::")}, nil}, + {"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress}, + {"tcp6", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress}, + {"tcp6", "::1", &TCPAddr{IP: IPv6loopback}, nil}, + {"tcp6", "::1", &UDPAddr{}, &AddrError{Err: "some error"}}, + {"tcp6", "::1", &UnixAddr{}, &AddrError{Err: "some error"}}, + + {"tcp", "127.0.0.1", nil, nil}, + {"tcp", "127.0.0.1", &TCPAddr{}, nil}, + {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, + {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, + {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, nil}, + {"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, nil}, + {"tcp", "127.0.0.1", &TCPAddr{IP: IPv6loopback}, errNoSuitableAddress}, + {"tcp", "127.0.0.1", &UDPAddr{}, &AddrError{Err: "some error"}}, + {"tcp", "127.0.0.1", &UnixAddr{}, &AddrError{Err: "some error"}}, + + {"tcp", "::1", nil, nil}, + {"tcp", "::1", &TCPAddr{}, nil}, + {"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0")}, nil}, + {"tcp", "::1", &TCPAddr{IP: ParseIP("0.0.0.0").To4()}, nil}, + {"tcp", "::1", &TCPAddr{IP: ParseIP("::")}, nil}, + {"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To4()}, errNoSuitableAddress}, + {"tcp", "::1", &TCPAddr{IP: ParseIP("127.0.0.1").To16()}, errNoSuitableAddress}, + {"tcp", "::1", &TCPAddr{IP: IPv6loopback}, nil}, + {"tcp", "::1", &UDPAddr{}, &AddrError{Err: "some error"}}, + {"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}}, + } + if supportsIPv4map { - dialerLocalAddrTests = append(dialerLocalAddrTests, dialerLocalAddrTest{ + tests = append(tests, test{ "tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, nil, }) } else { - dialerLocalAddrTests = append(dialerLocalAddrTests, dialerLocalAddrTest{ + tests = append(tests, test{ "tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, &AddrError{Err: "some error"}, }) } @@ -682,7 +673,7 @@ func TestDialerLocalAddr(t *testing.T) { } } - for _, tt := range dialerLocalAddrTests { + for _, tt := range tests { d := &Dialer{LocalAddr: tt.laddr} var addr string ip := ParseIP(tt.raddr) @@ -731,10 +722,7 @@ func TestDialerDualStack(t *testing.T) { var timeout = 150*time.Millisecond + closedPortDelay for _, dualstack := range []bool{false, true} { - dss, err := newDualStackServer([]streamListener{ - {network: "tcp4", address: "127.0.0.1"}, - {network: "tcp6", address: "::1"}, - }) + dss, err := newDualStackServer() if err != nil { t.Fatal(err) } diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index c1ef5a32d3dcd6..09bbd488660673 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -582,11 +582,11 @@ func TestIgnoreLameReferrals(t *testing.T) { } if got := len(addrs); got != 1 { - t.Fatal("got %d addresses, want 1", got) + t.Fatalf("got %d addresses, want 1", got) } if got, want := addrs[0].String(), "192.0.2.1"; got != want { - t.Fatal("got address %v, want %v", got, want) + t.Fatalf("got address %v, want %v", got, want) } } @@ -721,6 +721,6 @@ func TestIgnoreDNSForgeries(t *testing.T) { } if got := resp.answer[0].(*dnsRR_A).A; got != TestAddr { - t.Error("got address %v, want %v", got, TestAddr) + t.Errorf("got address %v, want %v", got, TestAddr) } } diff --git a/src/net/error_test.go b/src/net/error_test.go index 9f496d7d2db5e8..d6de5a3e68f048 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -762,3 +762,17 @@ func TestFileError(t *testing.T) { ln.Close() } } + +func parseLookupPortError(nestedErr error) error { + if nestedErr == nil { + return nil + } + + switch nestedErr.(type) { + case *AddrError, *DNSError: + return nil + case *os.PathError: // for Plan 9 + return nil + } + return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr) +} diff --git a/src/net/fd_plan9.go b/src/net/fd_plan9.go index 329d6152b2d989..7533232dc9b5eb 100644 --- a/src/net/fd_plan9.go +++ b/src/net/fd_plan9.go @@ -76,6 +76,9 @@ func (fd *netFD) Read(b []byte) (n int, err error) { return 0, err } defer fd.readUnlock() + if len(b) == 0 { + return 0, nil + } n, err = fd.data.Read(b) if isHangup(err) { err = io.EOF @@ -154,9 +157,7 @@ func (l *TCPListener) dup() (*os.File, error) { } func (fd *netFD) file(f *os.File, s string) (*os.File, error) { - syscall.ForkLock.RLock() dfd, err := syscall.Dup(int(f.Fd()), -1) - syscall.ForkLock.RUnlock() if err != nil { return nil, os.NewSyscallError("dup", err) } diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index 7ef10702ed8c08..0f80bc79ac270e 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -201,6 +201,14 @@ func (fd *netFD) Read(p []byte) (n int, err error) { return 0, err } defer fd.readUnlock() + if len(p) == 0 { + // If the caller wanted a zero byte read, return immediately + // without trying. (But after acquiring the readLock.) Otherwise + // syscall.Read returns 0, nil and eofError turns that into + // io.EOF. + // TODO(bradfitz): make it wait for readability? (Issue 15735) + return 0, nil + } if err := fd.pd.prepareRead(); err != nil { return 0, err } diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go index 49e79d6a950c04..b0b6769eb3cf74 100644 --- a/src/net/fd_windows.go +++ b/src/net/fd_windows.go @@ -427,7 +427,9 @@ func (fd *netFD) Read(buf []byte) (int, error) { if race.Enabled { race.Acquire(unsafe.Pointer(&ioSync)) } - err = fd.eofError(n, err) + if len(buf) != 0 { + err = fd.eofError(n, err) + } if _, ok := err.(syscall.Errno); ok { err = os.NewSyscallError("wsarecv", err) } diff --git a/src/net/file_plan9.go b/src/net/file_plan9.go index 892775a024f456..2939c09a43097f 100644 --- a/src/net/file_plan9.go +++ b/src/net/file_plan9.go @@ -50,9 +50,7 @@ func newFileFD(f *os.File) (net *netFD, err error) { name := comp[2] switch file := comp[n-1]; file { case "ctl", "clone": - syscall.ForkLock.RLock() fd, err := syscall.Dup(int(f.Fd()), -1) - syscall.ForkLock.RUnlock() if err != nil { return nil, os.NewSyscallError("dup", err) } @@ -60,7 +58,7 @@ func newFileFD(f *os.File) (net *netFD, err error) { dir := netdir + "/" + comp[n-2] ctl = os.NewFile(uintptr(fd), dir+"/"+file) - ctl.Seek(0, 0) + ctl.Seek(0, io.SeekStart) var buf [16]byte n, err := ctl.Read(buf[:]) if err != nil { diff --git a/src/net/http/client.go b/src/net/http/client.go index f8ab675a3de047..993c247eef536a 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -47,6 +47,9 @@ type Client struct { // method returns both the previous Response (with its Body // closed) and CheckRedirect's error (wrapped in a url.Error) // instead of issuing the Request req. + // As a special case, if CheckRedirect returns ErrUseLastResponse, + // then the most recent response is returned with its body + // unclosed, along with a nil error. // // If CheckRedirect is nil, the Client uses its default policy, // which is to stop after 10 consecutive requests. @@ -417,6 +420,12 @@ func (c *Client) Get(url string) (resp *Response, err error) { func alwaysFalse() bool { return false } +// ErrUseLastResponse can be returned by Client.CheckRedirect hooks to +// control how redirects are processed. If returned, the next request +// is not sent and the most recent response is returned with its body +// unclosed. +var ErrUseLastResponse = errors.New("net/http: use last response") + // checkRedirect calls either the user's configured CheckRedirect // function, or the default. func (c *Client) checkRedirect(req *Request, via []*Request) error { @@ -442,7 +451,7 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo req.closeBody() method := valueOrDefault(reqs[0].Method, "GET") var urlStr string - if resp != nil { + if resp != nil && resp.Request != nil { urlStr = resp.Request.URL.String() } else { urlStr = req.URL.String() @@ -467,11 +476,12 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo } ireq := reqs[0] req = &Request{ - Method: ireq.Method, - URL: u, - Header: make(Header), - Cancel: ireq.Cancel, - ctx: ireq.ctx, + Method: ireq.Method, + Response: resp, + URL: u, + Header: make(Header), + Cancel: ireq.Cancel, + ctx: ireq.ctx, } if ireq.Method == "POST" || ireq.Method == "PUT" { req.Method = "GET" @@ -481,7 +491,27 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo if ref := refererForURL(reqs[len(reqs)-1].URL, req.URL); ref != "" { req.Header.Set("Referer", ref) } - if err := c.checkRedirect(req, reqs); err != nil { + err = c.checkRedirect(req, reqs) + + // Sentinel error to let users select the + // previous response, without closing its + // body. See Issue 10069. + if err == ErrUseLastResponse { + return resp, nil + } + + // Close the previous response's body. But + // read at least some of the body so if it's + // small the underlying TCP connection will be + // re-used. No need to check for errors: if it + // fails, the Transport won't reuse it anyway. + const maxBodySlurpSize = 2 << 10 + if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize { + io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize) + } + resp.Body.Close() + + if err != nil { // Special case for Go 1 compatibility: return both the response // and an error if the CheckRedirect function failed. // See https://golang.org/issue/3795 @@ -508,14 +538,6 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo if !shouldRedirect(resp.StatusCode) { return resp, nil } - - // Read the body if small so underlying TCP connection will be re-used. - // No need to check for errors: if it fails, Transport won't reuse it anyway. - const maxBodySlurpSize = 2 << 10 - if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize { - io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize) - } - resp.Body.Close() } } diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index a9b30b1bf5c134..a9b1948005cb66 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -366,6 +366,44 @@ func TestPostRedirects(t *testing.T) { } } +func TestClientRedirectUseResponse(t *testing.T) { + defer afterTest(t) + const body = "Hello, world." + var ts *httptest.Server + ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + if strings.Contains(r.URL.Path, "/other") { + io.WriteString(w, "wrong body") + } else { + w.Header().Set("Location", ts.URL+"/other") + w.WriteHeader(StatusFound) + io.WriteString(w, body) + } + })) + defer ts.Close() + + c := &Client{CheckRedirect: func(req *Request, via []*Request) error { + if req.Response == nil { + t.Error("expected non-nil Request.Response") + } + return ErrUseLastResponse + }} + res, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != StatusFound { + t.Errorf("status = %d; want %d", res.StatusCode, StatusFound) + } + defer res.Body.Close() + slurp, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if string(slurp) != body { + t.Errorf("body = %q; want %q", slurp, body) + } +} + var expectedCookies = []*Cookie{ {Name: "ChocolateChip", Value: "tasty"}, {Name: "First", Value: "Hit"}, @@ -1168,3 +1206,26 @@ func TestReferer(t *testing.T) { } } } + +// issue15577Tripper returns a Response with a redirect response +// header and doesn't populate its Response.Request field. +type issue15577Tripper struct{} + +func (issue15577Tripper) RoundTrip(*Request) (*Response, error) { + resp := &Response{ + StatusCode: 303, + Header: map[string][]string{"Location": {"http://www.example.com/"}}, + Body: ioutil.NopCloser(strings.NewReader("")), + } + return resp, nil +} + +// Issue 15577: don't assume the roundtripper's response populates its Request field. +func TestClientRedirectResponseWithoutRequest(t *testing.T) { + c := &Client{ + CheckRedirect: func(*Request, []*Request) error { return fmt.Errorf("no redirects!") }, + Transport: issue15577Tripper{}, + } + // Check that this doesn't crash: + c.Get("http://dummy.tld") +} diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go index 9c3949fc3970f8..e12ea0c8c45b40 100644 --- a/src/net/http/clientserver_test.go +++ b/src/net/http/clientserver_test.go @@ -44,6 +44,13 @@ func (t *clientServerTest) close() { t.ts.Close() } +func (t *clientServerTest) scheme() string { + if t.h2 { + return "https" + } + return "http" +} + const ( h1Mode = false h2Mode = true @@ -229,11 +236,6 @@ func (tt h12Compare) normalizeRes(t *testing.T, res *Response, wantProto string) } slurp, err := ioutil.ReadAll(res.Body) - // TODO(bradfitz): short-term hack. Fix the - // http2 side of golang.org/issue/15366 once - // the http1 part is submitted. - res.Uncompressed = false - res.Body.Close() res.Body = slurpResult{ ReadCloser: ioutil.NopCloser(bytes.NewReader(slurp)), @@ -1176,12 +1178,6 @@ func TestH12_AutoGzipWithDumpResponse(t *testing.T) { io.WriteString(w, "\x1f\x8b\b\x00\x00\x00\x00\x00\x00\x00s\xf3\xf7\a\x00\xab'\xd4\x1a\x03\x00\x00\x00") }, EarlyCheckResponse: func(proto string, res *Response) { - if proto == "HTTP/2.0" { - // TODO(bradfitz): Fix the http2 side - // of golang.org/issue/15366 once the - // http1 part is submitted. - return - } if !res.Uncompressed { t.Errorf("%s: expected Uncompressed to be set", proto) } @@ -1200,6 +1196,35 @@ func TestH12_AutoGzipWithDumpResponse(t *testing.T) { }.run(t) } +// Issue 14607 +func TestCloseIdleConnections_h1(t *testing.T) { testCloseIdleConnections(t, h1Mode) } +func TestCloseIdleConnections_h2(t *testing.T) { testCloseIdleConnections(t, h2Mode) } +func testCloseIdleConnections(t *testing.T, h2 bool) { + defer afterTest(t) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("X-Addr", r.RemoteAddr) + })) + defer cst.close() + get := func() string { + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + v := res.Header.Get("X-Addr") + if v == "" { + t.Fatal("didn't get X-Addr") + } + return v + } + a1 := get() + cst.tr.CloseIdleConnections() + a2 := get() + if a1 == a2 { + t.Errorf("didn't close connection") + } +} + type noteCloseConn struct { net.Conn closeFunc func() diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index 3ebc51b19e62e9..9c5ba0809ad0ab 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -15,17 +15,16 @@ import ( ) var ( - DefaultUserAgent = defaultUserAgent - NewLoggingConn = newLoggingConn - ExportAppendTime = appendTime - ExportRefererForURL = refererForURL - ExportServerNewConn = (*Server).newConn - ExportCloseWriteAndWait = (*conn).closeWriteAndWait - ExportErrRequestCanceled = errRequestCanceled - ExportErrRequestCanceledConn = errRequestCanceledConn - ExportServeFile = serveFile - ExportHttp2ConfigureTransport = http2ConfigureTransport - ExportHttp2ConfigureServer = http2ConfigureServer + DefaultUserAgent = defaultUserAgent + NewLoggingConn = newLoggingConn + ExportAppendTime = appendTime + ExportRefererForURL = refererForURL + ExportServerNewConn = (*Server).newConn + ExportCloseWriteAndWait = (*conn).closeWriteAndWait + ExportErrRequestCanceled = errRequestCanceled + ExportErrRequestCanceledConn = errRequestCanceledConn + ExportServeFile = serveFile + ExportHttp2ConfigureServer = http2ConfigureServer ) func init() { @@ -152,3 +151,12 @@ func hookSetter(dst *func()) func(func()) { *dst = fn } } + +func ExportHttp2ConfigureTransport(t *Transport) error { + t2, err := http2configureTransport(t) + if err != nil { + return err + } + t.h2transport = t2 + return nil +} diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index 7cfe72a5dc8475..9cedcaa73daab2 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -20,15 +20,18 @@ import ( "bufio" "bytes" "compress/gzip" + "context" "crypto/tls" "encoding/binary" "errors" "fmt" "golang.org/x/net/http2/hpack" + "golang.org/x/net/lex/httplex" "io" "io/ioutil" "log" "net" + "net/http/httptrace" "net/textproto" "net/url" "os" @@ -47,6 +50,18 @@ type http2ClientConnPool interface { MarkDead(*http2ClientConn) } +// clientConnPoolIdleCloser is the interface implemented by ClientConnPool +// implementations which can close their idle connections. +type http2clientConnPoolIdleCloser interface { + http2ClientConnPool + closeIdleConnections() +} + +var ( + _ http2clientConnPoolIdleCloser = (*http2clientConnPool)(nil) + _ http2clientConnPoolIdleCloser = http2noDialClientConnPool{} +) + // TODO: use singleflight for dialing and addConnCalls? type http2clientConnPool struct { t *http2Transport @@ -247,6 +262,15 @@ func http2filterOutClientConn(in []*http2ClientConn, exclude *http2ClientConn) [ return out } +// noDialClientConnPool is an implementation of http2.ClientConnPool +// which never dials. We let the HTTP/1.1 client dial and use its TLS +// connection instead. +type http2noDialClientConnPool struct{ *http2clientConnPool } + +func (p http2noDialClientConnPool) GetClientConn(req *Request, addr string) (*http2ClientConn, error) { + return p.getClientConn(req, addr, http2noDialOnMiss) +} + func http2configureTransport(t1 *Transport) (*http2Transport, error) { connPool := new(http2clientConnPool) t2 := &http2Transport{ @@ -299,15 +323,6 @@ func http2registerHTTPSProtocol(t *Transport, rt RoundTripper) (err error) { return nil } -// noDialClientConnPool is an implementation of http2.ClientConnPool -// which never dials. We let the HTTP/1.1 client dial and use its TLS -// connection instead. -type http2noDialClientConnPool struct{ *http2clientConnPool } - -func (p http2noDialClientConnPool) GetClientConn(req *Request, addr string) (*http2ClientConn, error) { - return p.getClientConn(req, addr, http2noDialOnMiss) -} - // noDialH2RoundTripper is a RoundTripper which only tries to complete the request // if there's already has a cached connection to the host. type http2noDialH2RoundTripper struct{ t *http2Transport } @@ -1086,7 +1101,14 @@ func http2parseDataFrame(fh http2FrameHeader, payload []byte) (http2Frame, error return f, nil } -var http2errStreamID = errors.New("invalid streamid") +var ( + http2errStreamID = errors.New("invalid stream ID") + http2errDepStreamID = errors.New("invalid dependent stream ID") +) + +func http2validStreamIDOrZero(streamID uint32) bool { + return streamID&(1<<31) == 0 +} func http2validStreamID(streamID uint32) bool { return streamID != 0 && streamID&(1<<31) == 0 @@ -1452,8 +1474,8 @@ func (f *http2Framer) WriteHeaders(p http2HeadersFrameParam) error { } if !p.Priority.IsZero() { v := p.Priority.StreamDep - if !http2validStreamID(v) && !f.AllowIllegalWrites { - return errors.New("invalid dependent stream id") + if !http2validStreamIDOrZero(v) && !f.AllowIllegalWrites { + return http2errDepStreamID } if p.Priority.Exclusive { v |= 1 << 31 @@ -1521,6 +1543,9 @@ func (f *http2Framer) WritePriority(streamID uint32, p http2PriorityParam) error if !http2validStreamID(streamID) && !f.AllowIllegalWrites { return http2errStreamID } + if !http2validStreamIDOrZero(p.StreamDep) { + return http2errDepStreamID + } f.startWrite(http2FramePriority, 0, streamID) v := p.StreamDep if p.Exclusive { @@ -1852,7 +1877,7 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr hdec.SetEmitEnabled(true) hdec.SetMaxStringLength(fr.maxHeaderStringLen()) hdec.SetEmitFunc(func(hf hpack.HeaderField) { - if !http2validHeaderFieldValue(hf.Value) { + if !httplex.ValidHeaderFieldValue(hf.Value) { invalid = http2headerFieldValueError(hf.Value) } isPseudo := strings.HasPrefix(hf.Name, ":") @@ -1862,7 +1887,7 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr } } else { sawRegular = true - if !http2validHeaderFieldName(hf.Name) { + if !http2validWireHeaderFieldName(hf.Name) { invalid = http2headerFieldNameError(hf.Name) } } @@ -1962,7 +1987,88 @@ func http2summarizeFrame(f http2Frame) string { return buf.String() } -func http2requestCancel(req *Request) <-chan struct{} { return req.Cancel } +func http2transportExpectContinueTimeout(t1 *Transport) time.Duration { + return t1.ExpectContinueTimeout +} + +type http2contextContext interface { + context.Context +} + +func http2serverConnBaseContext(c net.Conn, opts *http2ServeConnOpts) (ctx http2contextContext, cancel func()) { + ctx, cancel = context.WithCancel(context.Background()) + ctx = context.WithValue(ctx, LocalAddrContextKey, c.LocalAddr()) + if hs := opts.baseConfig(); hs != nil { + ctx = context.WithValue(ctx, ServerContextKey, hs) + } + return +} + +func http2contextWithCancel(ctx http2contextContext) (_ http2contextContext, cancel func()) { + return context.WithCancel(ctx) +} + +func http2requestWithContext(req *Request, ctx http2contextContext) *Request { + return req.WithContext(ctx) +} + +type http2clientTrace httptrace.ClientTrace + +func http2reqContext(r *Request) context.Context { return r.Context() } + +func http2setResponseUncompressed(res *Response) { res.Uncompressed = true } + +func http2traceGotConn(req *Request, cc *http2ClientConn) { + trace := httptrace.ContextClientTrace(req.Context()) + if trace == nil || trace.GotConn == nil { + return + } + ci := httptrace.GotConnInfo{Conn: cc.tconn} + cc.mu.Lock() + ci.Reused = cc.nextStreamID > 1 + ci.WasIdle = len(cc.streams) == 0 && ci.Reused + if ci.WasIdle && !cc.lastActive.IsZero() { + ci.IdleTime = time.Now().Sub(cc.lastActive) + } + cc.mu.Unlock() + + trace.GotConn(ci) +} + +func http2traceWroteHeaders(trace *http2clientTrace) { + if trace != nil && trace.WroteHeaders != nil { + trace.WroteHeaders() + } +} + +func http2traceGot100Continue(trace *http2clientTrace) { + if trace != nil && trace.Got100Continue != nil { + trace.Got100Continue() + } +} + +func http2traceWait100Continue(trace *http2clientTrace) { + if trace != nil && trace.Wait100Continue != nil { + trace.Wait100Continue() + } +} + +func http2traceWroteRequest(trace *http2clientTrace, err error) { + if trace != nil && trace.WroteRequest != nil { + trace.WroteRequest(httptrace.WroteRequestInfo{Err: err}) + } +} + +func http2traceFirstResponseByte(trace *http2clientTrace) { + if trace != nil && trace.GotFirstResponseByte != nil { + trace.GotFirstResponseByte() + } +} + +func http2requestTrace(req *Request) *http2clientTrace { + trace := httptrace.ContextClientTrace(req.Context()) + return (*http2clientTrace)(trace) +} var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" @@ -2320,58 +2426,23 @@ var ( http2errInvalidHeaderFieldValue = errors.New("http2: invalid header field value") ) -// validHeaderFieldName reports whether v is a valid header field name (key). -// RFC 7230 says: -// header-field = field-name ":" OWS field-value OWS -// field-name = token -// token = 1*tchar -// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / -// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +// validWireHeaderFieldName reports whether v is a valid header field +// name (key). See httplex.ValidHeaderName for the base rules. +// // Further, http2 says: // "Just as in HTTP/1.x, header field names are strings of ASCII // characters that are compared in a case-insensitive // fashion. However, header field names MUST be converted to // lowercase prior to their encoding in HTTP/2. " -func http2validHeaderFieldName(v string) bool { +func http2validWireHeaderFieldName(v string) bool { if len(v) == 0 { return false } for _, r := range v { - if int(r) >= len(http2isTokenTable) || ('A' <= r && r <= 'Z') { - return false - } - if !http2isTokenTable[byte(r)] { + if !httplex.IsTokenRune(r) { return false } - } - return true -} - -// validHeaderFieldValue reports whether v is a valid header field value. -// -// RFC 7230 says: -// field-value = *( field-content / obs-fold ) -// obj-fold = N/A to http2, and deprecated -// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] -// field-vchar = VCHAR / obs-text -// obs-text = %x80-FF -// VCHAR = "any visible [USASCII] character" -// -// http2 further says: "Similarly, HTTP/2 allows header field values -// that are not valid. While most of the values that can be encoded -// will not alter header field parsing, carriage return (CR, ASCII -// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII -// 0x0) might be exploited by an attacker if they are translated -// verbatim. Any request or response that contains a character not -// permitted in a header field value MUST be treated as malformed -// (Section 8.1.2.6). Valid characters are defined by the -// field-content ABNF rule in Section 3.2 of [RFC7230]." -// -// This function does not (yet?) properly handle the rejection of -// strings that begin or end with SP or HTAB. -func http2validHeaderFieldValue(v string) bool { - for i := 0; i < len(v); i++ { - if b := v[i]; b < ' ' && b != '\t' || b == 0x7f { + if 'A' <= r && r <= 'Z' { return false } } @@ -2476,7 +2547,7 @@ func http2mustUint31(v int32) uint32 { } // bodyAllowedForStatus reports whether a given response status code -// permits a body. See RFC2616, section 4.4. +// permits a body. See RFC 2616, section 4.4. func http2bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: @@ -2502,86 +2573,6 @@ func (e *http2httpError) Temporary() bool { return true } var http2errTimeout error = &http2httpError{msg: "http2: timeout awaiting response headers", timeout: true} -var http2isTokenTable = [127]bool{ - '!': true, - '#': true, - '$': true, - '%': true, - '&': true, - '\'': true, - '*': true, - '+': true, - '-': true, - '.': true, - '0': true, - '1': true, - '2': true, - '3': true, - '4': true, - '5': true, - '6': true, - '7': true, - '8': true, - '9': true, - 'A': true, - 'B': true, - 'C': true, - 'D': true, - 'E': true, - 'F': true, - 'G': true, - 'H': true, - 'I': true, - 'J': true, - 'K': true, - 'L': true, - 'M': true, - 'N': true, - 'O': true, - 'P': true, - 'Q': true, - 'R': true, - 'S': true, - 'T': true, - 'U': true, - 'W': true, - 'V': true, - 'X': true, - 'Y': true, - 'Z': true, - '^': true, - '_': true, - '`': true, - 'a': true, - 'b': true, - 'c': true, - 'd': true, - 'e': true, - 'f': true, - 'g': true, - 'h': true, - 'i': true, - 'j': true, - 'k': true, - 'l': true, - 'm': true, - 'n': true, - 'o': true, - 'p': true, - 'q': true, - 'r': true, - 's': true, - 't': true, - 'u': true, - 'v': true, - 'w': true, - 'x': true, - 'y': true, - 'z': true, - '|': true, - '~': true, -} - type http2connectionStater interface { ConnectionState() tls.ConnectionState } @@ -2938,10 +2929,14 @@ func (o *http2ServeConnOpts) handler() Handler { // // The opts parameter is optional. If nil, default values are used. func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { + baseCtx, cancel := http2serverConnBaseContext(c, opts) + defer cancel() + sc := &http2serverConn{ srv: s, hs: opts.baseConfig(), conn: c, + baseCtx: baseCtx, remoteAddrStr: c.RemoteAddr().String(), bw: http2newBufferedWriter(c), handler: opts.handler(), @@ -2960,6 +2955,7 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { serveG: http2newGoroutineLock(), pushEnabled: true, } + sc.flow.add(http2initialWindowSize) sc.inflow.add(http2initialWindowSize) sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) @@ -3032,6 +3028,7 @@ type http2serverConn struct { conn net.Conn bw *http2bufferedWriter // writing to conn handler Handler + baseCtx http2contextContext framer *http2Framer doneServing chan struct{} // closed when serverConn.serve ends readFrameCh chan http2readFrameResult // written by serverConn.readFrames @@ -3095,10 +3092,12 @@ func (sc *http2serverConn) maxHeaderListSize() uint32 { // responseWriter's state field. type http2stream struct { // immutable: - sc *http2serverConn - id uint32 - body *http2pipe // non-nil if expecting DATA frames - cw http2closeWaiter // closed wait stream transitions to closed state + sc *http2serverConn + id uint32 + body *http2pipe // non-nil if expecting DATA frames + cw http2closeWaiter // closed wait stream transitions to closed state + ctx http2contextContext + cancelCtx func() // owned by serverConn's serve loop: bodyBytes int64 // body bytes seen so far @@ -3112,6 +3111,7 @@ type http2stream struct { sentReset bool // only true once detached from streams map gotReset bool // only true once detacted from streams map gotTrailerHeader bool // HEADER frame for trailers was seen + wroteHeaders bool // whether we wrote headers (not status 100) reqBuf []byte trailer Header // accumulated trailers @@ -3475,7 +3475,21 @@ func (sc *http2serverConn) writeFrameFromHandler(wm http2frameWriteMsg) error { // If you're not on the serve goroutine, use writeFrameFromHandler instead. func (sc *http2serverConn) writeFrame(wm http2frameWriteMsg) { sc.serveG.check() - sc.writeSched.add(wm) + + var ignoreWrite bool + + switch wm.write.(type) { + case *http2writeResHeaders: + wm.stream.wroteHeaders = true + case http2write100ContinueHeadersFrame: + if wm.stream.wroteHeaders { + ignoreWrite = true + } + } + + if !ignoreWrite { + sc.writeSched.add(wm) + } sc.scheduleFrameWrite() } @@ -3762,6 +3776,7 @@ func (sc *http2serverConn) processResetStream(f *http2RSTStreamFrame) error { } if st != nil { st.gotReset = true + st.cancelCtx() sc.closeStream(st, http2StreamError{f.StreamID, f.ErrCode}) } return nil @@ -3941,10 +3956,13 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { } sc.maxStreamID = id + ctx, cancelCtx := http2contextWithCancel(sc.baseCtx) st = &http2stream{ - sc: sc, - id: id, - state: http2stateOpen, + sc: sc, + id: id, + state: http2stateOpen, + ctx: ctx, + cancelCtx: cancelCtx, } if f.StreamEnded() { st.state = http2stateHalfClosedRemote @@ -3989,6 +4007,8 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { if f.Truncated { handler = http2handleHeaderListTooLong + } else if err := http2checkValidHTTP2Request(req); err != nil { + handler = http2new400Handler(err) } go sc.runHandler(rw, req, handler) @@ -4012,6 +4032,10 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { if st.trailer != nil { for _, hf := range f.RegularFields() { key := sc.canonicalHeader(hf.Name) + if !http2ValidTrailerHeader(key) { + + return http2StreamError{st.id, http2ErrCodeProtocol} + } st.trailer[key] = append(st.trailer[key], hf.Value) } } @@ -4150,6 +4174,7 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead Body: body, Trailer: trailer, } + req = http2requestWithContext(req, st.ctx) if bodyOpen { buf := make([]byte, http2initialWindowSize) @@ -4192,6 +4217,7 @@ func (sc *http2serverConn) getRequestBodyBuf() []byte { func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) { didPanic := true defer func() { + rw.rws.stream.cancelCtx() if didPanic { e := recover() // Same as net/http: @@ -4340,7 +4366,7 @@ type http2requestBody struct { func (b *http2requestBody) Close() error { if b.pipe != nil { - b.pipe.CloseWithError(http2errClosedBody) + b.pipe.BreakWithError(http2errClosedBody) } b.closed = true return nil @@ -4415,9 +4441,9 @@ func (rws *http2responseWriterState) hasTrailers() bool { return len(rws.trailer // written in the trailers at the end of the response. func (rws *http2responseWriterState) declareTrailer(k string) { k = CanonicalHeaderKey(k) - switch k { - case "Transfer-Encoding", "Content-Length", "Trailer": + if !http2ValidTrailerHeader(k) { + rws.conn.logf("ignoring invalid trailer %q", k) return } if !http2strSliceContains(rws.trailers, k) { @@ -4707,6 +4733,72 @@ func http2foreachHeaderElement(v string, fn func(string)) { } } +// From http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.2 +var http2connHeaders = []string{ + "Connection", + "Keep-Alive", + "Proxy-Connection", + "Transfer-Encoding", + "Upgrade", +} + +// checkValidHTTP2Request checks whether req is a valid HTTP/2 request, +// per RFC 7540 Section 8.1.2.2. +// The returned error is reported to users. +func http2checkValidHTTP2Request(req *Request) error { + for _, h := range http2connHeaders { + if _, ok := req.Header[h]; ok { + return fmt.Errorf("request header %q is not valid in HTTP/2", h) + } + } + te := req.Header["Te"] + if len(te) > 0 && (len(te) > 1 || (te[0] != "trailers" && te[0] != "")) { + return errors.New(`request header "TE" may only be "trailers" in HTTP/2`) + } + return nil +} + +func http2new400Handler(err error) HandlerFunc { + return func(w ResponseWriter, r *Request) { + Error(w, err.Error(), StatusBadRequest) + } +} + +// ValidTrailerHeader reports whether name is a valid header field name to appear +// in trailers. +// See: http://tools.ietf.org/html/rfc7230#section-4.1.2 +func http2ValidTrailerHeader(name string) bool { + name = CanonicalHeaderKey(name) + if strings.HasPrefix(name, "If-") || http2badTrailer[name] { + return false + } + return true +} + +var http2badTrailer = map[string]bool{ + "Authorization": true, + "Cache-Control": true, + "Connection": true, + "Content-Encoding": true, + "Content-Length": true, + "Content-Range": true, + "Content-Type": true, + "Expect": true, + "Host": true, + "Keep-Alive": true, + "Max-Forwards": true, + "Pragma": true, + "Proxy-Authenticate": true, + "Proxy-Authorization": true, + "Proxy-Connection": true, + "Range": true, + "Realm": true, + "Te": true, + "Trailer": true, + "Transfer-Encoding": true, + "Www-Authenticate": true, +} + const ( // transportDefaultConnFlow is how many connection-level flow control // tokens we give the server at start-up, past the default 64k. @@ -4833,6 +4925,8 @@ type http2ClientConn struct { bw *bufio.Writer br *bufio.Reader fr *http2Framer + lastActive time.Time + // Settings from peer: maxFrameSize uint32 maxConcurrentStreams uint32 @@ -4850,10 +4944,12 @@ type http2ClientConn struct { type http2clientStream struct { cc *http2ClientConn req *Request + trace *http2clientTrace // or nil ID uint32 resc chan http2resAndError bufPipe http2pipe // buffered pipe with the flow-controlled response payload requestedGzip bool + on100 func() // optional code to run if get a 100 continue response flow http2flow // guarded by cc.mu inflow http2flow // guarded by cc.mu @@ -4875,28 +4971,34 @@ type http2clientStream struct { } // awaitRequestCancel runs in its own goroutine and waits for the user -// to either cancel a RoundTrip request (using the provided -// Request.Cancel channel), or for the request to be done (any way it -// might be removed from the cc.streams map: peer reset, successful -// completion, TCP connection breakage, etc) -func (cs *http2clientStream) awaitRequestCancel(cancel <-chan struct{}) { - if cancel == nil { +// to cancel a RoundTrip request, its context to expire, or for the +// request to be done (any way it might be removed from the cc.streams +// map: peer reset, successful completion, TCP connection breakage, +// etc) +func (cs *http2clientStream) awaitRequestCancel(req *Request) { + ctx := http2reqContext(req) + if req.Cancel == nil && ctx.Done() == nil { return } select { - case <-cancel: + case <-req.Cancel: cs.bufPipe.CloseWithError(http2errRequestCanceled) cs.cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) + case <-ctx.Done(): + cs.bufPipe.CloseWithError(ctx.Err()) + cs.cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) case <-cs.done: } } -// checkReset reports any error sent in a RST_STREAM frame by the -// server. -func (cs *http2clientStream) checkReset() error { +// checkResetOrDone reports any error sent in a RST_STREAM frame by the +// server, or errStreamClosed if the stream is complete. +func (cs *http2clientStream) checkResetOrDone() error { select { case <-cs.peerReset: return cs.resetErr + case <-cs.done: + return http2errStreamClosed default: return nil } @@ -4964,6 +5066,7 @@ func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Res t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) return nil, err } + http2traceGotConn(req, cc) res, err := cc.RoundTrip(req) if http2shouldRetryRequest(req, err) { continue @@ -4980,7 +5083,7 @@ func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Res // connected from previous requests but are now sitting idle. // It does not interrupt any connections currently in use. func (t *http2Transport) CloseIdleConnections() { - if cp, ok := t.connPool().(*http2clientConnPool); ok { + if cp, ok := t.connPool().(http2clientConnPoolIdleCloser); ok { cp.closeIdleConnections() } } @@ -5057,6 +5160,13 @@ func (t *http2Transport) disableKeepAlives() bool { return t.t1 != nil && t.t1.DisableKeepAlives } +func (t *http2Transport) expectContinueTimeout() time.Duration { + if t.t1 == nil { + return 0 + } + return http2transportExpectContinueTimeout(t.t1) +} + func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) { if http2VerboseLogs { t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr()) @@ -5254,6 +5364,30 @@ func http2checkConnHeaders(req *Request) error { return nil } +func http2bodyAndLength(req *Request) (body io.Reader, contentLen int64) { + body = req.Body + if body == nil { + return nil, 0 + } + if req.ContentLength != 0 { + return req.Body, req.ContentLength + } + + // We have a body but a zero content length. Test to see if + // it's actually zero or just unset. + var buf [1]byte + n, rerr := io.ReadFull(body, buf[:]) + if rerr != nil && rerr != io.EOF { + return http2errorReader{rerr}, -1 + } + if n == 1 { + + return io.MultiReader(bytes.NewReader(buf[:]), body), -1 + } + + return nil, 0 +} + func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { if err := http2checkConnHeaders(req); err != nil { return nil, err @@ -5265,67 +5399,62 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { } hasTrailers := trailers != "" - var body io.Reader = req.Body - contentLen := req.ContentLength - if req.Body != nil && contentLen == 0 { - // Test to see if it's actually zero or just unset. - var buf [1]byte - n, rerr := io.ReadFull(body, buf[:]) - if rerr != nil && rerr != io.EOF { - contentLen = -1 - body = http2errorReader{rerr} - } else if n == 1 { - - contentLen = -1 - body = io.MultiReader(bytes.NewReader(buf[:]), body) - } else { - - body = nil - } - } + body, contentLen := http2bodyAndLength(req) + hasBody := body != nil cc.mu.Lock() + cc.lastActive = time.Now() if cc.closed || !cc.canTakeNewRequestLocked() { cc.mu.Unlock() return nil, http2errClientConnUnusable } - cs := cc.newStream() - cs.req = req - hasBody := body != nil - + // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? + var requestedGzip bool if !cc.t.disableCompression() && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" && req.Method != "HEAD" { - cs.requestedGzip = true + requestedGzip = true } - hdrs := cc.encodeHeaders(req, cs.requestedGzip, trailers, contentLen) + hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen) + if err != nil { + cc.mu.Unlock() + return nil, err + } + + cs := cc.newStream() + cs.req = req + cs.trace = http2requestTrace(req) + cs.requestedGzip = requestedGzip + bodyWriter := cc.t.getBodyWriterState(cs, body) + cs.on100 = bodyWriter.on100 + cc.wmu.Lock() endStream := !hasBody && !hasTrailers werr := cc.writeHeaders(cs.ID, endStream, hdrs) cc.wmu.Unlock() + http2traceWroteHeaders(cs.trace) cc.mu.Unlock() if werr != nil { if hasBody { req.Body.Close() + bodyWriter.cancel() } cc.forgetStreamID(cs.ID) + http2traceWroteRequest(cs.trace, werr) return nil, werr } var respHeaderTimer <-chan time.Time - var bodyCopyErrc chan error // result of body copy if hasBody { - bodyCopyErrc = make(chan error, 1) - go func() { - bodyCopyErrc <- cs.writeRequestBody(body, req.Body) - }() + bodyWriter.scheduleBodyWrite() } else { + http2traceWroteRequest(cs.trace, nil) if d := cc.responseHeaderTimeout(); d != 0 { timer := time.NewTimer(d) defer timer.Stop() @@ -5334,8 +5463,8 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { } readLoopResCh := cs.resc - requestCanceledCh := http2requestCancel(req) bodyWritten := false + ctx := http2reqContext(req) for { select { @@ -5343,6 +5472,7 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { res := re.res if re.err != nil || res.StatusCode > 299 { + bodyWriter.cancel() cs.abortRequestBodyWrite(http2errStopReqBodyWrite) } if re.err != nil { @@ -5357,21 +5487,32 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { if !hasBody || bodyWritten { cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) } else { + bodyWriter.cancel() cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) } return nil, http2errTimeout - case <-requestCanceledCh: + case <-ctx.Done(): + cc.forgetStreamID(cs.ID) + if !hasBody || bodyWritten { + cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) + } else { + bodyWriter.cancel() + cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) + } + return nil, ctx.Err() + case <-req.Cancel: cc.forgetStreamID(cs.ID) if !hasBody || bodyWritten { cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) } else { + bodyWriter.cancel() cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) } return nil, http2errRequestCanceled case <-cs.peerReset: return nil, cs.resetErr - case err := <-bodyCopyErrc: + case err := <-bodyWriter.resc: if err != nil { return nil, err } @@ -5429,6 +5570,7 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos defer cc.putFrameScratchBuffer(buf) defer func() { + http2traceWroteRequest(cs.trace, err) cerr := bodyCloser.Close() if err == nil { @@ -5516,7 +5658,7 @@ func (cs *http2clientStream) awaitFlowControl(maxBytes int) (taken int32, err er if cs.stopReqBody != nil { return 0, cs.stopReqBody } - if err := cs.checkReset(); err != nil { + if err := cs.checkResetOrDone(); err != nil { return 0, err } if a := cs.flow.available(); a > 0 { @@ -5543,7 +5685,7 @@ type http2badStringError struct { func (e *http2badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) } // requires cc.mu be held. -func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trailers string, contentLength int64) []byte { +func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { cc.hbuf.Reset() host := req.Host @@ -5551,6 +5693,17 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail host = req.URL.Host } + for k, vv := range req.Header { + if !httplex.ValidHeaderFieldName(k) { + return nil, fmt.Errorf("invalid HTTP header name %q", k) + } + for _, v := range vv { + if !httplex.ValidHeaderFieldValue(v) { + return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) + } + } + } + cc.writeHeader(":authority", host) cc.writeHeader(":method", req.Method) if req.Method != "CONNECT" { @@ -5568,7 +5721,7 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail case "host", "content-length": continue - case "connection", "proxy-connection", "transfer-encoding", "upgrade": + case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive": continue case "user-agent": @@ -5595,7 +5748,7 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail if !didUA { cc.writeHeader("user-agent", http2defaultUserAgent) } - return cc.hbuf.Bytes() + return cc.hbuf.Bytes(), nil } // shouldSendReqContentLength reports whether the http2.Transport should send @@ -5671,8 +5824,10 @@ func (cc *http2ClientConn) streamByID(id uint32, andRemove bool) *http2clientStr defer cc.mu.Unlock() cs := cc.streams[id] if andRemove && cs != nil && !cc.closed { + cc.lastActive = time.Now() delete(cc.streams, id) close(cs.done) + cc.cond.Broadcast() } return cs } @@ -5794,6 +5949,10 @@ func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) erro } else { return rl.processTrailers(cs, f) } + if cs.trace != nil { + + http2traceFirstResponseByte(cs.trace) + } res, err := rl.handleResponse(cs, f) if err != nil { @@ -5839,7 +5998,10 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http } if statusCode == 100 { - + http2traceGot100Continue(cs.trace) + if cs.on100 != nil { + cs.on100() + } cs.pastHeaders = false return nil, nil } @@ -5892,13 +6054,14 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http cs.bufPipe = http2pipe{b: buf} cs.bytesRemain = res.ContentLength res.Body = http2transportResponseBody{cs} - go cs.awaitRequestCancel(http2requestCancel(cs.req)) + go cs.awaitRequestCancel(cs.req) if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { res.Header.Del("Content-Encoding") res.Header.Del("Content-Length") res.ContentLength = -1 res.Body = &http2gzipReader{body: res.Body} + http2setResponseUncompressed(res) } return res, nil } @@ -6248,6 +6411,79 @@ type http2errorReader struct{ err error } func (r http2errorReader) Read(p []byte) (int, error) { return 0, r.err } +// bodyWriterState encapsulates various state around the Transport's writing +// of the request body, particularly regarding doing delayed writes of the body +// when the request contains "Expect: 100-continue". +type http2bodyWriterState struct { + cs *http2clientStream + timer *time.Timer // if non-nil, we're doing a delayed write + fnonce *sync.Once // to call fn with + fn func() // the code to run in the goroutine, writing the body + resc chan error // result of fn's execution + delay time.Duration // how long we should delay a delayed write for +} + +func (t *http2Transport) getBodyWriterState(cs *http2clientStream, body io.Reader) (s http2bodyWriterState) { + s.cs = cs + if body == nil { + return + } + resc := make(chan error, 1) + s.resc = resc + s.fn = func() { + resc <- cs.writeRequestBody(body, cs.req.Body) + } + s.delay = t.expectContinueTimeout() + if s.delay == 0 || + !httplex.HeaderValuesContainsToken( + cs.req.Header["Expect"], + "100-continue") { + return + } + s.fnonce = new(sync.Once) + + // Arm the timer with a very large duration, which we'll + // intentionally lower later. It has to be large now because + // we need a handle to it before writing the headers, but the + // s.delay value is defined to not start until after the + // request headers were written. + const hugeDuration = 365 * 24 * time.Hour + s.timer = time.AfterFunc(hugeDuration, func() { + s.fnonce.Do(s.fn) + }) + return +} + +func (s http2bodyWriterState) cancel() { + if s.timer != nil { + s.timer.Stop() + } +} + +func (s http2bodyWriterState) on100() { + if s.timer == nil { + + return + } + s.timer.Stop() + go func() { s.fnonce.Do(s.fn) }() +} + +// scheduleBodyWrite starts writing the body, either immediately (in +// the common case) or after the delay timeout. It should not be +// called until after the headers have been written. +func (s http2bodyWriterState) scheduleBodyWrite() { + if s.timer == nil { + + go s.fn() + return + } + http2traceWait100Continue(s.cs.trace) + if s.timer.Stop() { + s.timer.Reset(s.delay) + } +} + // writeFramer is implemented by any type that is used to write frames. type http2writeFramer interface { writeFrame(http2writeContext) error @@ -6470,13 +6706,13 @@ func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) { for _, k := range keys { vv := h[k] k = http2lowerHeader(k) - if !http2validHeaderFieldName(k) { + if !http2validWireHeaderFieldName(k) { continue } isTE := k == "transfer-encoding" for _, v := range vv { - if !http2validHeaderFieldValue(v) { + if !httplex.ValidHeaderFieldValue(v) { continue } diff --git a/src/net/http/http.go b/src/net/http/http.go index a121628632f99a..4d088a5bb1c812 100644 --- a/src/net/http/http.go +++ b/src/net/http/http.go @@ -6,6 +6,8 @@ package http import ( "strings" + + "golang.org/x/net/lex/httplex" ) // maxInt64 is the effective "infinite" value for the Server and @@ -35,3 +37,7 @@ func removeEmptyPort(host string) string { } return host } + +func isNotToken(r rune) bool { + return !httplex.IsTokenRune(r) +} diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go index b1f49541d576e5..0ad26a3d418005 100644 --- a/src/net/http/httptest/recorder.go +++ b/src/net/http/httptest/recorder.go @@ -6,6 +6,7 @@ package httptest import ( "bytes" + "io/ioutil" "net/http" ) @@ -17,9 +18,8 @@ type ResponseRecorder struct { Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to Flushed bool - stagingMap http.Header // map that handlers manipulate to set headers - trailerMap http.Header // lazily filled when Trailers() is called - + result *http.Response // cache of Result's return value + snapHeader http.Header // snapshot of HeaderMap at first Write wroteHeader bool } @@ -38,10 +38,10 @@ const DefaultRemoteAddr = "1.2.3.4" // Header returns the response headers. func (rw *ResponseRecorder) Header() http.Header { - m := rw.stagingMap + m := rw.HeaderMap if m == nil { m = make(http.Header) - rw.stagingMap = m + rw.HeaderMap = m } return m } @@ -104,11 +104,17 @@ func (rw *ResponseRecorder) WriteHeader(code int) { if rw.HeaderMap == nil { rw.HeaderMap = make(http.Header) } - for k, vv := range rw.stagingMap { + rw.snapHeader = cloneHeader(rw.HeaderMap) +} + +func cloneHeader(h http.Header) http.Header { + h2 := make(http.Header, len(h)) + for k, vv := range h { vv2 := make([]string, len(vv)) copy(vv2, vv) - rw.HeaderMap[k] = vv2 + h2[k] = vv2 } + return h2 } // Flush sets rw.Flushed to true. @@ -119,32 +125,61 @@ func (rw *ResponseRecorder) Flush() { rw.Flushed = true } -// Trailers returns any trailers set by the handler. It must be called -// after the handler finished running. -func (rw *ResponseRecorder) Trailers() http.Header { - if rw.trailerMap != nil { - return rw.trailerMap - } - trailers, ok := rw.HeaderMap["Trailer"] - if !ok { - rw.trailerMap = make(http.Header) - return rw.trailerMap - } - rw.trailerMap = make(http.Header, len(trailers)) - for _, k := range trailers { - switch k { - case "Transfer-Encoding", "Content-Length", "Trailer": - // Ignore since forbidden by RFC 2616 14.40. - continue - } - k = http.CanonicalHeaderKey(k) - vv, ok := rw.stagingMap[k] - if !ok { - continue +// Result returns the response generated by the handler. +// +// The returned Response will have at least its StatusCode, +// Header, Body, and optionally Trailer populated. +// More fields may be populated in the future, so callers should +// not DeepEqual the result in tests. +// +// The Response.Header is a snapshot of the headers at the time of the +// first write call, or at the time of this call, if the handler never +// did a write. +// +// Result must only be called after the handler has finished running. +func (rw *ResponseRecorder) Result() *http.Response { + if rw.result != nil { + return rw.result + } + if rw.snapHeader == nil { + rw.snapHeader = cloneHeader(rw.HeaderMap) + } + res := &http.Response{ + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + StatusCode: rw.Code, + Header: rw.snapHeader, + } + rw.result = res + if res.StatusCode == 0 { + res.StatusCode = 200 + } + res.Status = http.StatusText(res.StatusCode) + if rw.Body != nil { + res.Body = ioutil.NopCloser(bytes.NewReader(rw.Body.Bytes())) + } + + if trailers, ok := rw.snapHeader["Trailer"]; ok { + res.Trailer = make(http.Header, len(trailers)) + for _, k := range trailers { + // TODO: use http2.ValidTrailerHeader, but we can't + // get at it easily because it's bundled into net/http + // unexported. This is good enough for now: + switch k { + case "Transfer-Encoding", "Content-Length", "Trailer": + // Ignore since forbidden by RFC 2616 14.40. + continue + } + k = http.CanonicalHeaderKey(k) + vv, ok := rw.HeaderMap[k] + if !ok { + continue + } + vv2 := make([]string, len(vv)) + copy(vv2, vv) + res.Trailer[k] = vv2 } - vv2 := make([]string, len(vv)) - copy(vv2, vv) - rw.trailerMap[k] = vv2 } - return rw.trailerMap + return res } diff --git a/src/net/http/httptest/recorder_test.go b/src/net/http/httptest/recorder_test.go index 19a37b6c54d2d6..d4e7137913e492 100644 --- a/src/net/http/httptest/recorder_test.go +++ b/src/net/http/httptest/recorder_test.go @@ -23,6 +23,14 @@ func TestRecorder(t *testing.T) { return nil } } + hasResultStatus := func(wantCode int) checkFunc { + return func(rec *ResponseRecorder) error { + if rec.Result().StatusCode != wantCode { + return fmt.Errorf("Result().StatusCode = %d; want %d", rec.Result().StatusCode, wantCode) + } + return nil + } + } hasContents := func(want string) checkFunc { return func(rec *ResponseRecorder) error { if rec.Body.String() != want { @@ -39,10 +47,18 @@ func TestRecorder(t *testing.T) { return nil } } - hasHeader := func(key, want string) checkFunc { + hasOldHeader := func(key, want string) checkFunc { return func(rec *ResponseRecorder) error { if got := rec.HeaderMap.Get(key); got != want { - return fmt.Errorf("header %s = %q; want %q", key, got, want) + return fmt.Errorf("HeaderMap header %s = %q; want %q", key, got, want) + } + return nil + } + } + hasHeader := func(key, want string) checkFunc { + return func(rec *ResponseRecorder) error { + if got := rec.Result().Header.Get(key); got != want { + return fmt.Errorf("final header %s = %q; want %q", key, got, want) } return nil } @@ -50,9 +66,9 @@ func TestRecorder(t *testing.T) { hasNotHeaders := func(keys ...string) checkFunc { return func(rec *ResponseRecorder) error { for _, k := range keys { - _, ok := rec.HeaderMap[http.CanonicalHeaderKey(k)] + v, ok := rec.Result().Header[http.CanonicalHeaderKey(k)] if ok { - return fmt.Errorf("unexpected header %s", k) + return fmt.Errorf("unexpected header %s with value %q", k, v) } } return nil @@ -60,7 +76,7 @@ func TestRecorder(t *testing.T) { } hasTrailer := func(key, want string) checkFunc { return func(rec *ResponseRecorder) error { - if got := rec.Trailers().Get(key); got != want { + if got := rec.Result().Trailer.Get(key); got != want { return fmt.Errorf("trailer %s = %q; want %q", key, got, want) } return nil @@ -68,7 +84,7 @@ func TestRecorder(t *testing.T) { } hasNotTrailers := func(keys ...string) checkFunc { return func(rec *ResponseRecorder) error { - trailers := rec.Trailers() + trailers := rec.Result().Trailer for _, k := range keys { _, ok := trailers[http.CanonicalHeaderKey(k)] if ok { @@ -194,6 +210,40 @@ func TestRecorder(t *testing.T) { hasNotTrailers("Non-Trailer", "Trailer-B", "Trailer-NotDeclared"), ), }, + { + "Header set without any write", // Issue 15560 + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Foo", "1") + + // Simulate somebody using + // new(ResponseRecorder) instead of + // using the constructor which sets + // this to 200 + w.(*ResponseRecorder).Code = 0 + }, + check( + hasOldHeader("X-Foo", "1"), + hasStatus(0), + hasHeader("X-Foo", "1"), + hasResultStatus(200), + ), + }, + { + "HeaderMap vs FinalHeaders", // more for Issue 15560 + func(w http.ResponseWriter, r *http.Request) { + h := w.Header() + h.Set("X-Foo", "1") + w.Write([]byte("hi")) + h.Set("X-Foo", "2") + h.Set("X-Bar", "2") + }, + check( + hasOldHeader("X-Foo", "2"), + hasOldHeader("X-Bar", "2"), + hasHeader("X-Foo", "1"), + hasNotHeaders("X-Bar"), + ), + }, } r, _ := http.NewRequest("GET", "http://foo.com/", nil) for _, tt := range tests { diff --git a/src/net/http/httptrace/trace.go b/src/net/http/httptrace/trace.go index 5d2c548b3cc310..6f187a7b694a84 100644 --- a/src/net/http/httptrace/trace.go +++ b/src/net/http/httptrace/trace.go @@ -90,6 +90,7 @@ type ClientTrace struct { // connection reuse is disabled via Transport.DisableKeepAlives. // PutIdleConn is called before the caller's Response.Body.Close // call returns. + // For HTTP/2, this hook is not currently used. PutIdleConn func(err error) // GotFirstResponseByte is called when the first byte of the response diff --git a/src/net/http/httptrace/trace_test.go b/src/net/http/httptrace/trace_test.go index ed6ddbb40dbd17..c7eaed83d47851 100644 --- a/src/net/http/httptrace/trace_test.go +++ b/src/net/http/httptrace/trace_test.go @@ -16,7 +16,7 @@ func TestCompose(t *testing.T) { connectStart := func(b byte) func(network, addr string) { return func(network, addr string) { if addr != "addr" { - t.Errorf(`%d. args for %Q case = %q, %q; want addr of "addr"`, testNum, b, network, addr) + t.Errorf(`%d. args for %q case = %q, %q; want addr of "addr"`, testNum, b, network, addr) } buf.WriteByte(b) } diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 44d15ff6be7f08..49c120afde12fc 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -90,6 +90,10 @@ func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { } else { req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery } + if _, ok := req.Header["User-Agent"]; !ok { + // explicitly disable User-Agent so it's not set to default value + req.Header.Set("User-Agent", "") + } } return &ReverseProxy{Director: director} } diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index e9c0658271f06c..fe7cdb888f5ced 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -348,6 +348,49 @@ func TestNilBody(t *testing.T) { } } +// Issue 15524 +func TestUserAgentHeader(t *testing.T) { + const explicitUA = "explicit UA" + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/noua" { + if c := r.Header.Get("User-Agent"); c != "" { + t.Errorf("handler got non-empty User-Agent header %q", c) + } + return + } + if c := r.Header.Get("User-Agent"); c != explicitUA { + t.Errorf("handler got unexpected User-Agent header %q", c) + } + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := NewSingleHostReverseProxy(backendURL) + proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + getReq.Header.Set("User-Agent", explicitUA) + getReq.Close = true + res, err := http.DefaultClient.Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } + res.Body.Close() + + getReq, _ = http.NewRequest("GET", frontend.URL+"/noua", nil) + getReq.Header.Set("User-Agent", "") + getReq.Close = true + res, err = http.DefaultClient.Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } + res.Body.Close() +} + type bufferPool struct { get func() []byte put func([]byte) diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go index a136dc99a65fc8..9abe1ab6d9d674 100644 --- a/src/net/http/internal/chunked_test.go +++ b/src/net/http/internal/chunked_test.go @@ -122,7 +122,7 @@ func TestChunkReaderAllocs(t *testing.T) { byter := bytes.NewReader(buf.Bytes()) bufr := bufio.NewReader(byter) mallocs := testing.AllocsPerRun(100, func() { - byter.Seek(0, 0) + byter.Seek(0, io.SeekStart) bufr.Reset(byter) r := NewChunkedReader(bufr) n, err := io.ReadFull(r, readBuf) diff --git a/src/net/http/request.go b/src/net/http/request.go index 1bde114909c38e..e8780dea943c97 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -255,6 +255,11 @@ type Request struct { // set, it is undefined whether Cancel is respected. Cancel <-chan struct{} + // Response is the redirect response which caused this request + // to be created. This field is only populated during client + // redirects. + Response *Response + // ctx is either the client or server context. It should only // be modified via copying the whole Request using WithContext. // It is unexported to prevent people from using Context wrong @@ -885,68 +890,56 @@ func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { } type maxBytesReader struct { - w ResponseWriter - r io.ReadCloser // underlying reader - n int64 // max bytes remaining - stopped bool - sawEOF bool + w ResponseWriter + r io.ReadCloser // underlying reader + n int64 // max bytes remaining + err error // sticky error } func (l *maxBytesReader) tooLarge() (n int, err error) { - if !l.stopped { - l.stopped = true - - // The server code and client code both use - // maxBytesReader. This "requestTooLarge" check is - // only used by the server code. To prevent binaries - // which only using the HTTP Client code (such as - // cmd/go) from also linking in the HTTP server, don't - // use a static type assertion to the server - // "*response" type. Check this interface instead: - type requestTooLarger interface { - requestTooLarge() - } - if res, ok := l.w.(requestTooLarger); ok { - res.requestTooLarge() - } - } - return 0, errors.New("http: request body too large") + l.err = errors.New("http: request body too large") + return 0, l.err } func (l *maxBytesReader) Read(p []byte) (n int, err error) { - toRead := l.n - if l.n == 0 { - if l.sawEOF { - return l.tooLarge() - } - // The underlying io.Reader may not return (0, io.EOF) - // at EOF if the requested size is 0, so read 1 byte - // instead. The io.Reader docs are a bit ambiguous - // about the return value of Read when 0 bytes are - // requested, and {bytes,strings}.Reader gets it wrong - // too (it returns (0, nil) even at EOF). - toRead = 1 + if l.err != nil { + return 0, l.err + } + if len(p) == 0 { + return 0, nil } - if int64(len(p)) > toRead { - p = p[:toRead] + // If they asked for a 32KB byte read but only 5 bytes are + // remaining, no need to read 32KB. 6 bytes will answer the + // question of the whether we hit the limit or go past it. + if int64(len(p)) > l.n+1 { + p = p[:l.n+1] } n, err = l.r.Read(p) - if err == io.EOF { - l.sawEOF = true - } - if l.n == 0 { - // If we had zero bytes to read remaining (but hadn't seen EOF) - // and we get a byte here, that means we went over our limit. - if n > 0 { - return l.tooLarge() - } - return 0, err + + if int64(n) <= l.n { + l.n -= int64(n) + l.err = err + return n, err + } + + n = int(l.n) + l.n = 0 + + // The server code and client code both use + // maxBytesReader. This "requestTooLarge" check is + // only used by the server code. To prevent binaries + // which only using the HTTP Client code (such as + // cmd/go) from also linking in the HTTP server, don't + // use a static type assertion to the server + // "*response" type. Check this interface instead: + type requestTooLarger interface { + requestTooLarge() } - l.n -= int64(n) - if l.n < 0 { - l.n = 0 + if res, ok := l.w.(requestTooLarger); ok { + res.requestTooLarge() } - return + l.err = errors.New("http: request body too large") + return n, l.err } func (l *maxBytesReader) Close() error { diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go index 82c7af3cda8dbe..a4c88c02915ccd 100644 --- a/src/net/http/request_test.go +++ b/src/net/http/request_test.go @@ -679,6 +679,46 @@ func TestIssue10884_MaxBytesEOF(t *testing.T) { } } +// Issue 14981: MaxBytesReader's return error wasn't sticky. It +// doesn't technically need to be, but people expected it to be. +func TestMaxBytesReaderStickyError(t *testing.T) { + isSticky := func(r io.Reader) error { + var log bytes.Buffer + buf := make([]byte, 1000) + var firstErr error + for { + n, err := r.Read(buf) + fmt.Fprintf(&log, "Read(%d) = %d, %v\n", len(buf), n, err) + if err == nil { + continue + } + if firstErr == nil { + firstErr = err + continue + } + if !reflect.DeepEqual(err, firstErr) { + return fmt.Errorf("non-sticky error. got log:\n%s", log.Bytes()) + } + t.Logf("Got log: %s", log.Bytes()) + return nil + } + } + tests := [...]struct { + readable int + limit int64 + }{ + 0: {99, 100}, + 1: {100, 100}, + 2: {101, 100}, + } + for i, tt := range tests { + rc := MaxBytesReader(nil, ioutil.NopCloser(bytes.NewReader(make([]byte, tt.readable))), tt.limit) + if err := isSticky(rc); err != nil { + t.Errorf("%d. error: %v", i, err) + } + } +} + func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { diff --git a/src/net/http/response.go b/src/net/http/response.go index 0164a09c6a0d2a..979651c08a28b6 100644 --- a/src/net/http/response.go +++ b/src/net/http/response.go @@ -96,7 +96,7 @@ type Response struct { // any trailer values sent by the server. Trailer Header - // The Request that was sent to obtain this Response. + // Request is the request that was sent to obtain this Response. // Request's Body is nil (having already been consumed). // This is only populated for Client requests. Request *Request diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 661f355d0dd709..c32ff299029a49 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -11,6 +11,7 @@ import ( "bytes" "context" "crypto/tls" + "encoding/json" "errors" "fmt" "internal/testenv" @@ -714,6 +715,31 @@ func testTCPConnectionCloses(t *testing.T, req string, h Handler) { } } +func testTCPConnectionStaysOpen(t *testing.T, req string, handler Handler) { + defer afterTest(t) + ts := httptest.NewServer(handler) + defer ts.Close() + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + br := bufio.NewReader(conn) + for i := 0; i < 2; i++ { + if _, err := io.WriteString(conn, req); err != nil { + t.Fatal(err) + } + res, err := ReadResponse(br, nil) + if err != nil { + t.Fatalf("res %d: %v", i+1, err) + } + if _, err := io.Copy(ioutil.Discard, res.Body); err != nil { + t.Fatalf("res %d body copy: %v", i+1, err) + } + res.Body.Close() + } +} + // TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive. func TestServeHTTP10Close(t *testing.T) { testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { @@ -749,6 +775,54 @@ func TestHTTP2UpgradeClosesConnection(t *testing.T) { })) } +func send204(w ResponseWriter, r *Request) { w.WriteHeader(204) } +func send304(w ResponseWriter, r *Request) { w.WriteHeader(304) } + +// Issue 15647: 204 responses can't have bodies, so HTTP/1.0 keep-alive conns should stay open. +func TestHTTP10KeepAlive204Response(t *testing.T) { + testTCPConnectionStaysOpen(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(send204)) +} + +func TestHTTP11KeepAlive204Response(t *testing.T) { + testTCPConnectionStaysOpen(t, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n", HandlerFunc(send204)) +} + +func TestHTTP10KeepAlive304Response(t *testing.T) { + testTCPConnectionStaysOpen(t, + "GET / HTTP/1.0\r\nConnection: keep-alive\r\nIf-Modified-Since: Mon, 02 Jan 2006 15:04:05 GMT\r\n\r\n", + HandlerFunc(send304)) +} + +// Issue 15703 +func TestKeepAliveFinalChunkWithEOF(t *testing.T) { + defer afterTest(t) + cst := newClientServerTest(t, false /* h1 */, HandlerFunc(func(w ResponseWriter, r *Request) { + w.(Flusher).Flush() // force chunked encoding + w.Write([]byte("{\"Addr\": \"" + r.RemoteAddr + "\"}")) + })) + defer cst.close() + type data struct { + Addr string + } + var addrs [2]data + for i := range addrs { + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatal(err) + } + if err := json.NewDecoder(res.Body).Decode(&addrs[i]); err != nil { + t.Fatal(err) + } + if addrs[i].Addr == "" { + t.Fatal("no address") + } + res.Body.Close() + } + if addrs[0] != addrs[1] { + t.Fatalf("connection not reused") + } +} + func TestSetsRemoteAddr_h1(t *testing.T) { testSetsRemoteAddr(t, h1Mode) } func TestSetsRemoteAddr_h2(t *testing.T) { testSetsRemoteAddr(t, h2Mode) } @@ -3990,10 +4064,16 @@ func TestServerValidatesHeaders(t *testing.T) { } } -func TestServerRequestContextCancel_ServeHTTPDone(t *testing.T) { +func TestServerRequestContextCancel_ServeHTTPDone_h1(t *testing.T) { + testServerRequestContextCancel_ServeHTTPDone(t, h1Mode) +} +func TestServerRequestContextCancel_ServeHTTPDone_h2(t *testing.T) { + testServerRequestContextCancel_ServeHTTPDone(t, h2Mode) +} +func testServerRequestContextCancel_ServeHTTPDone(t *testing.T, h2 bool) { defer afterTest(t) ctxc := make(chan context.Context, 1) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { ctx := r.Context() select { case <-ctx.Done(): @@ -4002,8 +4082,8 @@ func TestServerRequestContextCancel_ServeHTTPDone(t *testing.T) { } ctxc <- ctx })) - defer ts.Close() - res, err := Get(ts.URL) + defer cst.close() + res, err := cst.c.Get(cst.ts.URL) if err != nil { t.Fatal(err) } @@ -4056,9 +4136,15 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) { } } -func TestServerContext_ServerContextKey(t *testing.T) { +func TestServerContext_ServerContextKey_h1(t *testing.T) { + testServerContext_ServerContextKey(t, h1Mode) +} +func TestServerContext_ServerContextKey_h2(t *testing.T) { + testServerContext_ServerContextKey(t, h2Mode) +} +func testServerContext_ServerContextKey(t *testing.T, h2 bool) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { ctx := r.Context() got := ctx.Value(ServerContextKey) if _, ok := got.(*Server); !ok { @@ -4066,12 +4152,14 @@ func TestServerContext_ServerContextKey(t *testing.T) { } got = ctx.Value(LocalAddrContextKey) - if _, ok := got.(net.Addr); !ok { + if addr, ok := got.(net.Addr); !ok { t.Errorf("local addr value = %T; want net.Addr", got) + } else if fmt.Sprint(addr) != r.Host { + t.Errorf("local addr = %v; want %v", addr, r.Host) } })) - defer ts.Close() - res, err := Get(ts.URL) + defer cst.close() + res, err := cst.c.Get(cst.ts.URL) if err != nil { t.Fatal(err) } @@ -4268,7 +4356,7 @@ func BenchmarkClient(b *testing.B) { // Wait for the server process to respond. url := "http://localhost:" + port + "/" for i := 0; i < 100; i++ { - time.Sleep(50 * time.Millisecond) + time.Sleep(100 * time.Millisecond) if _, err := getNoBody(url); err == nil { break } diff --git a/src/net/http/server.go b/src/net/http/server.go index 23fb84fcdabf41..1a8c0fc6cc27ba 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -27,6 +27,8 @@ import ( "sync" "sync/atomic" "time" + + "golang.org/x/net/lex/httplex" ) // Errors used by the HTTP server. @@ -91,10 +93,24 @@ type ResponseWriter interface { Header() Header // Write writes the data to the connection as part of an HTTP reply. - // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) - // before writing the data. If the Header does not contain a - // Content-Type line, Write adds a Content-Type set to the result of passing - // the initial 512 bytes of written data to DetectContentType. + // + // If WriteHeader has not yet been called, Write calls + // WriteHeader(http.StatusOK) before writing the data. If the Header + // does not contain a Content-Type line, Write adds a Content-Type set + // to the result of passing the initial 512 bytes of written data to + // DetectContentType. + // + // Depending on the HTTP protocol version and the client, calling + // Write or WriteHeader may prevent future reads on the + // Request.Body. For HTTP/1.x requests, handlers should read any + // needed request body data before writing the response. Once the + // headers have been flushed (due to either an explicit Flusher.Flush + // call or writing enough data to trigger a flush), the request body + // may be unavailable. For HTTP/2 requests, the Go HTTP server permits + // handlers to continue to read the request body while concurrently + // writing the response. However, such behavior may not be supported + // by all HTTP/2 clients. Handlers should read before writing if + // possible to maximize compatibility. Write([]byte) (int, error) // WriteHeader sends an HTTP response header with status code. @@ -769,15 +785,15 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) { if len(hosts) > 1 { return nil, badRequestError("too many Host headers") } - if len(hosts) == 1 && !validHostHeader(hosts[0]) { + if len(hosts) == 1 && !httplex.ValidHostHeader(hosts[0]) { return nil, badRequestError("malformed Host header") } for k, vv := range req.Header { - if !validHeaderName(k) { + if !httplex.ValidHeaderFieldName(k) { return nil, badRequestError("invalid header name") } for _, v := range vv { - if !validHeaderValue(v) { + if !httplex.ValidHeaderFieldValue(v) { return nil, badRequestError("invalid header value") } } @@ -994,7 +1010,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // Check for a explicit (and valid) Content-Length header. hasCL := w.contentLength != -1 - if w.wants10KeepAlive && (isHEAD || hasCL) { + if w.wants10KeepAlive && (isHEAD || hasCL || !bodyAllowedForStatus(w.status)) { _, connectionHeaderSet := header["Connection"] if !connectionHeaderSet { setHeader.connection = "keep-alive" @@ -1027,6 +1043,9 @@ func (cw *chunkWriter) writeHeader(p []byte) { // replying, if the handler hasn't already done so. But we // don't want to do an unbounded amount of reading here for // DoS reasons, so we only try up to a threshold. + // TODO(bradfitz): where does RFC 2616 say that? See Issue 15527 + // about HTTP/1.x Handlers concurrently reading and writing, like + // HTTP/2 handlers can do. Maybe this code should be relaxed? if w.req.ContentLength != 0 && !w.closeAfterReply { var discard, tooBig bool @@ -2065,7 +2084,7 @@ type Server struct { MaxHeaderBytes int // TLSNextProto optionally specifies a function to take over - // ownership of the provided TLS connection when an NPN + // ownership of the provided TLS connection when an NPN/ALPN // protocol upgrade has occurred. The map key is the protocol // name negotiated. The Handler argument should be used to // handle HTTP requests and will initialize the Request's TLS diff --git a/src/net/http/status.go b/src/net/http/status.go index f3dacab6a92043..98645b7d746637 100644 --- a/src/net/http/status.go +++ b/src/net/http/status.go @@ -4,63 +4,79 @@ package http -// HTTP status codes, defined in RFC 2616. +// HTTP status codes as registered with IANA. +// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml const ( - StatusContinue = 100 - StatusSwitchingProtocols = 101 + StatusContinue = 100 // RFC 7231, 6.2.1 + StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 + StatusProcessing = 102 // RFC 2518, 10.1 - StatusOK = 200 - StatusCreated = 201 - StatusAccepted = 202 - StatusNonAuthoritativeInfo = 203 - StatusNoContent = 204 - StatusResetContent = 205 - StatusPartialContent = 206 + StatusOK = 200 // RFC 7231, 6.3.1 + StatusCreated = 201 // RFC 7231, 6.3.2 + StatusAccepted = 202 // RFC 7231, 6.3.3 + StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4 + StatusNoContent = 204 // RFC 7231, 6.3.5 + StatusResetContent = 205 // RFC 7231, 6.3.6 + StatusPartialContent = 206 // RFC 7233, 4.1 + StatusMultiStatus = 207 // RFC 4918, 11.1 + StatusAlreadyReported = 208 // RFC 5842, 7.1 + StatusIMUsed = 226 // RFC 3229, 10.4.1 - StatusMultipleChoices = 300 - StatusMovedPermanently = 301 - StatusFound = 302 - StatusSeeOther = 303 - StatusNotModified = 304 - StatusUseProxy = 305 - StatusTemporaryRedirect = 307 + StatusMultipleChoices = 300 // RFC 7231, 6.4.1 + StatusMovedPermanently = 301 // RFC 7231, 6.4.2 + StatusFound = 302 // RFC 7231, 6.4.3 + StatusSeeOther = 303 // RFC 7231, 6.4.4 + StatusNotModified = 304 // RFC 7232, 4.1 + StatusUseProxy = 305 // RFC 7231, 6.4.5 + _ = 306 // RFC 7231, 6.4.6 (Unused) + StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 + StatusPermanentRedirect = 308 // RFC 7538, 3 - StatusBadRequest = 400 - StatusUnauthorized = 401 - StatusPaymentRequired = 402 - StatusForbidden = 403 - StatusNotFound = 404 - StatusMethodNotAllowed = 405 - StatusNotAcceptable = 406 - StatusProxyAuthRequired = 407 - StatusRequestTimeout = 408 - StatusConflict = 409 - StatusGone = 410 - StatusLengthRequired = 411 - StatusPreconditionFailed = 412 - StatusRequestEntityTooLarge = 413 - StatusRequestURITooLong = 414 - StatusUnsupportedMediaType = 415 - StatusRequestedRangeNotSatisfiable = 416 - StatusExpectationFailed = 417 - StatusTeapot = 418 - StatusPreconditionRequired = 428 - StatusTooManyRequests = 429 - StatusRequestHeaderFieldsTooLarge = 431 - StatusUnavailableForLegalReasons = 451 + StatusBadRequest = 400 // RFC 7231, 6.5.1 + StatusUnauthorized = 401 // RFC 7235, 3.1 + StatusPaymentRequired = 402 // RFC 7231, 6.5.2 + StatusForbidden = 403 // RFC 7231, 6.5.3 + StatusNotFound = 404 // RFC 7231, 6.5.4 + StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 + StatusNotAcceptable = 406 // RFC 7231, 6.5.6 + StatusProxyAuthRequired = 407 // RFC 7235, 3.2 + StatusRequestTimeout = 408 // RFC 7231, 6.5.7 + StatusConflict = 409 // RFC 7231, 6.5.8 + StatusGone = 410 // RFC 7231, 6.5.9 + StatusLengthRequired = 411 // RFC 7231, 6.5.10 + StatusPreconditionFailed = 412 // RFC 7232, 4.2 + StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 + StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 + StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 + StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 + StatusExpectationFailed = 417 // RFC 7231, 6.5.14 + StatusTeapot = 418 // RFC 7168, 2.3.3 + StatusUnprocessableEntity = 422 // RFC 4918, 11.2 + StatusLocked = 423 // RFC 4918, 11.3 + StatusFailedDependency = 424 // RFC 4918, 11.4 + StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 + StatusPreconditionRequired = 428 // RFC 6585, 3 + StatusTooManyRequests = 429 // RFC 6585, 4 + StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 + StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 - StatusInternalServerError = 500 - StatusNotImplemented = 501 - StatusBadGateway = 502 - StatusServiceUnavailable = 503 - StatusGatewayTimeout = 504 - StatusHTTPVersionNotSupported = 505 - StatusNetworkAuthenticationRequired = 511 + StatusInternalServerError = 500 // RFC 7231, 6.6.1 + StatusNotImplemented = 501 // RFC 7231, 6.6.2 + StatusBadGateway = 502 // RFC 7231, 6.6.3 + StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 + StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 + StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 + StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 + StatusInsufficientStorage = 507 // RFC 4918, 11.5 + StatusLoopDetected = 508 // RFC 5842, 7.2 + StatusNotExtended = 510 // RFC 2774, 7 + StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 ) var statusText = map[int]string{ StatusContinue: "Continue", StatusSwitchingProtocols: "Switching Protocols", + StatusProcessing: "Processing", StatusOK: "OK", StatusCreated: "Created", @@ -69,6 +85,9 @@ var statusText = map[int]string{ StatusNoContent: "No Content", StatusResetContent: "Reset Content", StatusPartialContent: "Partial Content", + StatusMultiStatus: "Multi-Status", + StatusAlreadyReported: "Already Reported", + StatusIMUsed: "IM Used", StatusMultipleChoices: "Multiple Choices", StatusMovedPermanently: "Moved Permanently", @@ -77,6 +96,7 @@ var statusText = map[int]string{ StatusNotModified: "Not Modified", StatusUseProxy: "Use Proxy", StatusTemporaryRedirect: "Temporary Redirect", + StatusPermanentRedirect: "Permanent Redirect", StatusBadRequest: "Bad Request", StatusUnauthorized: "Unauthorized", @@ -97,6 +117,10 @@ var statusText = map[int]string{ StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", StatusExpectationFailed: "Expectation Failed", StatusTeapot: "I'm a teapot", + StatusUnprocessableEntity: "Unprocessable Entity", + StatusLocked: "Locked", + StatusFailedDependency: "Failed Dependency", + StatusUpgradeRequired: "Upgrade Required", StatusPreconditionRequired: "Precondition Required", StatusTooManyRequests: "Too Many Requests", StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large", @@ -108,6 +132,10 @@ var statusText = map[int]string{ StatusServiceUnavailable: "Service Unavailable", StatusGatewayTimeout: "Gateway Timeout", StatusHTTPVersionNotSupported: "HTTP Version Not Supported", + StatusVariantAlsoNegotiates: "Variant Also Negotiates", + StatusInsufficientStorage: "Insufficient Storage", + StatusLoopDetected: "Loop Detected", + StatusNotExtended: "Not Extended", StatusNetworkAuthenticationRequired: "Network Authentication Required", } diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go index 501e4be08cba59..b27ace638a191f 100644 --- a/src/net/http/transfer.go +++ b/src/net/http/transfer.go @@ -17,6 +17,8 @@ import ( "strconv" "strings" "sync" + + "golang.org/x/net/lex/httplex" ) // ErrLineTooLong is returned when reading request or response bodies @@ -561,9 +563,9 @@ func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool { } conv := header["Connection"] - hasClose := headerValuesContainsToken(conv, "close") + hasClose := httplex.HeaderValuesContainsToken(conv, "close") if major == 1 && minor == 0 { - return hasClose || !headerValuesContainsToken(conv, "keep-alive") + return hasClose || !httplex.HeaderValuesContainsToken(conv, "keep-alive") } if hasClose && removeCloseHeader { diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 7fdd94e05bc768..43b20f2da2f86a 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -26,6 +26,8 @@ import ( "strings" "sync" "time" + + "golang.org/x/net/lex/httplex" ) // DefaultTransport is the default implementation of Transport and is @@ -35,10 +37,10 @@ import ( // $no_proxy) environment variables. var DefaultTransport RoundTripper = &Transport{ Proxy: ProxyFromEnvironment, - Dialer: &net.Dialer{ + DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, - }, + }).DialContext, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, @@ -85,18 +87,18 @@ type Transport struct { // If Proxy is nil or returns a nil *URL, no proxy is used. Proxy func(*Request) (*url.URL, error) - // Dial specifies the dial function for creating unencrypted - // TCP connections. If Dial and Dialer are both nil, net.Dial - // is used. + // DialContext specifies the dial function for creating unencrypted TCP connections. + // If DialContext is nil (and the deprecated Dial below is also nil), + // then the transport dials using package net. + DialContext func(ctx context.Context, network, addr string) (net.Conn, error) + + // Dial specifies the dial function for creating unencrypted TCP connections. // - // Deprecated: Use Dialer instead. If both are specified, Dialer - // takes precedence. + // Deprecated: Use DialContext instead, which allows the transport + // to cancel dials as soon as they are no longer needed. + // If both are set, DialContext takes priority. Dial func(network, addr string) (net.Conn, error) - // Dialer optionally specifies a dialer configuration to use - // for new connections. - Dialer *net.Dialer - // DialTLS specifies an optional dial function for creating // TLS connections for non-proxied HTTPS requests. // @@ -195,22 +197,12 @@ func (t *Transport) onceSetNextProtoDefaults() { // Transport. return } - if t.TLSClientConfig != nil { - // Be conservative for now (for Go 1.6) at least and - // don't automatically enable http2 if they've - // specified a custom TLS config. Let them opt-in - // themselves via http2.ConfigureTransport so we don't - // surprise them by modifying their tls.Config. - // Issue 14275. - return - } - if t.ExpectContinueTimeout != 0 && t != DefaultTransport { - // ExpectContinueTimeout is unsupported in http2, so - // if they explicitly asked for it (as opposed to just - // using the DefaultTransport, which sets it), then - // disable http2 for now. - // - // Issue 13851. (and changed in Issue 14391) + if t.TLSClientConfig != nil || t.Dial != nil || t.DialTLS != nil { + // Be conservative and don't automatically enable + // http2 if they've specified a custom TLS config or + // custom dialers. Let them opt-in themselves via + // http2.ConfigureTransport so we don't surprise them + // by modifying their tls.Config. Issue 14275. return } t2, err := http2configureTransport(t) @@ -325,11 +317,11 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { isHTTP := scheme == "http" || scheme == "https" if isHTTP { for k, vv := range req.Header { - if !validHeaderName(k) { + if !httplex.ValidHeaderFieldName(k) { return nil, fmt.Errorf("net/http: invalid header field name %q", k) } for _, v := range vv { - if !validHeaderValue(v) { + if !httplex.ValidHeaderFieldValue(v) { return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k) } } @@ -387,47 +379,47 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { if err == nil { return resp, nil } - if err := checkTransportResend(err, req, pconn); err != nil { + if !pconn.shouldRetryRequest(req, err) { return nil, err } testHookRoundTripRetried() } } -// checkTransportResend checks whether a failed HTTP request can be -// resent on a new connection. The non-nil input error is the error from -// roundTrip, which might be wrapped in a beforeRespHeaderError error. -// -// The return value is either nil to retry the request, the provided -// err unmodified, or the unwrapped error inside a -// beforeRespHeaderError. -func checkTransportResend(err error, req *Request, pconn *persistConn) error { - brhErr, ok := err.(beforeRespHeaderError) - if !ok { - return err +// shouldRetryRequest reports whether we should retry sending a failed +// HTTP request on a new connection. The non-nil input error is the +// error from roundTrip. +func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool { + if err == errMissingHost { + // User error. + return false } - err = brhErr.error // unwrap the custom error in case we return it - if err != errMissingHost && pconn.isReused() && req.isReplayable() { - // If we try to reuse a connection that the server is in the process of - // closing, we may end up successfully writing out our request (or a - // portion of our request) only to find a connection error when we try to - // read from (or finish writing to) the socket. - - // There can be a race between the socket pool checking whether a socket - // is still connected, receiving the FIN, and sending/reading data on a - // reused socket. If we receive the FIN between the connectedness check - // and writing/reading from the socket, we may first learn the socket is - // disconnected when we get a ERR_SOCKET_NOT_CONNECTED. This will most - // likely happen when trying to retrieve its IP address. See - // http://crbug.com/105824 for more details. - - // We resend a request only if we reused a keep-alive connection and did - // not yet receive any header data. This automatically prevents an - // infinite resend loop because we'll run out of the cached keep-alive - // connections eventually. - return nil + if !pc.isReused() { + // This was a fresh connection. There's no reason the server + // should've hung up on us. + // + // Also, if we retried now, we could loop forever + // creating new connections and retrying if the server + // is just hanging up on us because it doesn't like + // our request (as opposed to sending an error). + return false } - return err + if !req.isReplayable() { + // Don't retry non-idempotent requests. + + // TODO: swap the nothingWrittenError and isReplayable checks, + // putting the "if nothingWrittenError => return true" case + // first, per golang.org/issue/15723 + return false + } + if _, ok := err.(nothingWrittenError); ok { + // We never wrote anything, so it's safe to retry. + return true + } + if err == errServerClosedIdle || err == errServerClosedConn { + return true + } + return false // conservatively } // ErrSkipAltProtocol is a sentinel error value defined by Transport.RegisterProtocol. @@ -570,7 +562,8 @@ var ( errTooManyIdleHost = errors.New("http: putIdleConn: too many idle connections for host") errCloseIdleConns = errors.New("http: CloseIdleConnections called") errReadLoopExiting = errors.New("http: persistConn.readLoop exiting") - errServerClosedIdle = errors.New("http: server closed idle conn") + errServerClosedIdle = errors.New("http: server closed idle connection") + errServerClosedConn = errors.New("http: server closed connection") errIdleConnTimeout = errors.New("http: idle connection timeout") ) @@ -784,8 +777,8 @@ func (t *Transport) replaceReqCanceler(r *Request, fn func()) bool { var zeroDialer net.Dialer func (t *Transport) dial(ctx context.Context, network, addr string) (net.Conn, error) { - if t.Dialer != nil { - return t.Dialer.DialContext(ctx, network, addr) + if t.DialContext != nil { + return t.DialContext(ctx, network, addr) } if t.Dial != nil { c, err := t.Dial(network, addr) @@ -852,7 +845,7 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC select { case v := <-dialc: // Our dial finished. - if trace != nil && trace.GotConn != nil && v.pc != nil { + if trace != nil && trace.GotConn != nil && v.pc != nil && v.pc.alt == nil { trace.GotConn(httptrace.GotConnInfo{Conn: v.pc.conn}) } return v.pc, v.err @@ -881,12 +874,13 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistConn, error) { pconn := &persistConn{ - t: t, - cacheKey: cm.key(), - reqch: make(chan requestAndChan, 1), - writech: make(chan writeRequest, 1), - closech: make(chan struct{}), - writeErrCh: make(chan error, 1), + t: t, + cacheKey: cm.key(), + reqch: make(chan requestAndChan, 1), + writech: make(chan writeRequest, 1), + closech: make(chan struct{}), + writeErrCh: make(chan error, 1), + writeLoopDone: make(chan struct{}), } tlsDial := t.DialTLS != nil && cm.targetScheme == "https" && cm.proxyURL == nil if tlsDial { @@ -1003,12 +997,28 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon } pconn.br = bufio.NewReader(pconn) - pconn.bw = bufio.NewWriter(pconn.conn) + pconn.bw = bufio.NewWriter(persistConnWriter{pconn}) go pconn.readLoop() go pconn.writeLoop() return pconn, nil } +// persistConnWriter is the io.Writer written to by pc.bw. +// It accumulates the number of bytes written to the underlying conn, +// so the retry logic can determine whether any bytes made it across +// the wire. +// This is exactly 1 pointer field wide so it can go into an interface +// without allocation. +type persistConnWriter struct { + pc *persistConn +} + +func (w persistConnWriter) Write(p []byte) (n int, err error) { + n, err = w.pc.conn.Write(p) + w.pc.nwrite += int64(n) + return +} + // useProxy reports whether requests to addr should use a proxy, // according to the NO_PROXY or no_proxy environment variable. // addr is always a canonicalAddr with a host and port. @@ -1142,6 +1152,7 @@ type persistConn struct { tlsState *tls.ConnectionState br *bufio.Reader // from conn bw *bufio.Writer // to conn + nwrite int64 // bytes written reqch chan requestAndChan // written by roundTrip; read by readLoop writech chan writeRequest // written by roundTrip; read by writeLoop closech chan struct{} // closed when conn closed @@ -1154,6 +1165,8 @@ type persistConn struct { // whether or not a connection can be reused. Issue 7569. writeErrCh chan error + writeLoopDone chan struct{} // closed when write loop ends + // Both guarded by Transport.idleMu: idleAt time.Time // time it last become idle idleTimer *time.Timer // holding an AfterFunc to close it @@ -1195,7 +1208,7 @@ func (pc *persistConn) Read(p []byte) (n int, err error) { // isBroken reports whether this connection is in a known broken state. func (pc *persistConn) isBroken() bool { pc.mu.Lock() - b := pc.broken + b := pc.closed != nil pc.mu.Unlock() return b } @@ -1221,7 +1234,9 @@ func (pc *persistConn) gotIdleConnTrace(idleAt time.Time) (t httptrace.GotConnIn t.Reused = pc.reused t.Conn = pc.conn t.WasIdle = true - t.IdleTime = time.Since(idleAt) + if !idleAt.IsZero() { + t.IdleTime = time.Since(idleAt) + } return } @@ -1247,6 +1262,56 @@ func (pc *persistConn) closeConnIfStillIdle() { pc.close(errIdleConnTimeout) } +// mapRoundTripErrorFromReadLoop maps the provided readLoop error into +// the error value that should be returned from persistConn.roundTrip. +// +// The startBytesWritten value should be the value of pc.nwrite before the roundTrip +// started writing the request. +func (pc *persistConn) mapRoundTripErrorFromReadLoop(startBytesWritten int64, err error) (out error) { + if err == nil { + return nil + } + if pc.isCanceled() { + return errRequestCanceled + } + if err == errServerClosedIdle || err == errServerClosedConn { + return err + } + if pc.isBroken() { + <-pc.writeLoopDone + if pc.nwrite == startBytesWritten { + return nothingWrittenError{err} + } + } + return err +} + +// mapRoundTripErrorAfterClosed returns the error value to be propagated +// up to Transport.RoundTrip method when persistConn.roundTrip sees +// its pc.closech channel close, indicating the persistConn is dead. +// (after closech is closed, pc.closed is valid). +func (pc *persistConn) mapRoundTripErrorAfterClosed(startBytesWritten int64) error { + if pc.isCanceled() { + return errRequestCanceled + } + err := pc.closed + if err == errServerClosedIdle || err == errServerClosedConn { + // Don't decorate + return err + } + + // Wait for the writeLoop goroutine to terminated, and then + // see if we actually managed to write anything. If not, we + // can retry the request. + <-pc.writeLoopDone + if pc.nwrite == startBytesWritten { + return nothingWrittenError{err} + } + + return fmt.Errorf("net/http: HTTP/1.x transport connection broken: %v", err) + +} + func (pc *persistConn) readLoop() { closeErr := errReadLoopExiting // default value, if not changed below defer func() { @@ -1283,9 +1348,6 @@ func (pc *persistConn) readLoop() { for alive { pc.readLimit = pc.maxHeaderResponseSize() _, err := pc.br.Peek(1) - if err != nil { - err = beforeRespHeaderError{err} - } pc.mu.Lock() if pc.numExpectedResponses == 0 { @@ -1301,12 +1363,16 @@ func (pc *persistConn) readLoop() { var resp *Response if err == nil { resp, err = pc.readResponse(rc, trace) + } else { + err = errServerClosedConn + closeErr = err } if err != nil { if pc.readLimit <= 0 { err = fmt.Errorf("net/http: server response headers exceeded %d bytes; aborted", pc.maxHeaderResponseSize()) } + // If we won't be able to retry this request later (from the // roundTrip goroutine), mark it as done now. // BEFORE the send on rc.ch, as the client might re-use the @@ -1314,7 +1380,7 @@ func (pc *persistConn) readLoop() { // t.setReqCanceler from this persistConn while the Transport // potentially spins up a different persistConn for the // caller's subsequent request. - if checkTransportResend(err, rc.req, pc) != nil { + if !pc.shouldRetryRequest(rc.req, err) { pc.t.setReqCanceler(rc.req, nil) } select { @@ -1501,24 +1567,33 @@ func (pc *persistConn) waitForContinue(continueCh <-chan struct{}) func() bool { } } +// nothingWrittenError wraps a write errors which ended up writing zero bytes. +type nothingWrittenError struct { + error +} + func (pc *persistConn) writeLoop() { + defer close(pc.writeLoopDone) for { select { case wr := <-pc.writech: - if pc.isBroken() { - wr.ch <- errors.New("http: can't write HTTP request on broken connection") - continue - } + startBytesWritten := pc.nwrite err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra, pc.waitForContinue(wr.continueCh)) if err == nil { err = pc.bw.Flush() } if err != nil { - pc.markBroken() wr.req.Request.closeBody() + if pc.nwrite == startBytesWritten { + err = nothingWrittenError{err} + } } pc.writeErrCh <- err // to the body reader, which might recycle us wr.ch <- err // to the roundTrip function + if err != nil { + pc.close(err) + return + } case <-pc.closech: return } @@ -1619,12 +1694,6 @@ var ( testHookReadLoopBeforeNextRead = nop ) -// beforeRespHeaderError is used to indicate when an IO error has occurred before -// any header data was received. -type beforeRespHeaderError struct { - error -} - func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { testHookEnterRoundTrip() if !pc.t.replaceReqCanceler(req.Request, pc.cancelRequest) { @@ -1680,6 +1749,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err // Write the request concurrently with waiting for a response, // in case the server decides to reply before reading our full // request body. + startBytesWritten := pc.nwrite writeErrCh := make(chan error, 1) pc.writech <- writeRequest{req, writeErrCh, continueCh} @@ -1704,7 +1774,7 @@ WaitResponse: if pc.isCanceled() { err = errRequestCanceled } - re = responseAndError{err: beforeRespHeaderError{err}} + re = responseAndError{err: err} pc.close(fmt.Errorf("write error: %v", err)) break WaitResponse } @@ -1714,22 +1784,14 @@ WaitResponse: respHeaderTimer = timer.C } case <-pc.closech: - var err error - if pc.isCanceled() { - err = errRequestCanceled - } else { - err = beforeRespHeaderError{fmt.Errorf("net/http: HTTP/1 transport connection broken: %v", pc.closed)} - } - re = responseAndError{err: err} + re = responseAndError{err: pc.mapRoundTripErrorAfterClosed(startBytesWritten)} break WaitResponse case <-respHeaderTimer: pc.close(errTimeout) re = responseAndError{err: errTimeout} break WaitResponse case re = <-resc: - if re.err != nil && pc.isCanceled() { - re.err = errRequestCanceled - } + re.err = pc.mapRoundTripErrorFromReadLoop(startBytesWritten, re.err) break WaitResponse case <-cancelChan: pc.t.CancelRequest(req.Request) @@ -1749,15 +1811,6 @@ WaitResponse: return re.res, re.err } -// markBroken marks a connection as broken (so it's not reused). -// It differs from close in that it doesn't close the underlying -// connection for use when it's still being read. -func (pc *persistConn) markBroken() { - pc.mu.Lock() - defer pc.mu.Unlock() - pc.broken = true -} - // markReused marks this connection as having been successfully used for a // request and response. func (pc *persistConn) markReused() { @@ -1952,25 +2005,27 @@ func cloneTLSConfig(cfg *tls.Config) *tls.Config { return &tls.Config{} } return &tls.Config{ - Rand: cfg.Rand, - Time: cfg.Time, - Certificates: cfg.Certificates, - NameToCertificate: cfg.NameToCertificate, - GetCertificate: cfg.GetCertificate, - RootCAs: cfg.RootCAs, - NextProtos: cfg.NextProtos, - ServerName: cfg.ServerName, - ClientAuth: cfg.ClientAuth, - ClientCAs: cfg.ClientCAs, - InsecureSkipVerify: cfg.InsecureSkipVerify, - CipherSuites: cfg.CipherSuites, - PreferServerCipherSuites: cfg.PreferServerCipherSuites, - SessionTicketsDisabled: cfg.SessionTicketsDisabled, - SessionTicketKey: cfg.SessionTicketKey, - ClientSessionCache: cfg.ClientSessionCache, - MinVersion: cfg.MinVersion, - MaxVersion: cfg.MaxVersion, - CurvePreferences: cfg.CurvePreferences, + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + SessionTicketsDisabled: cfg.SessionTicketsDisabled, + SessionTicketKey: cfg.SessionTicketKey, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, + Renegotiation: cfg.Renegotiation, } } @@ -1983,24 +2038,25 @@ func cloneTLSClientConfig(cfg *tls.Config) *tls.Config { return &tls.Config{} } return &tls.Config{ - Rand: cfg.Rand, - Time: cfg.Time, - Certificates: cfg.Certificates, - NameToCertificate: cfg.NameToCertificate, - GetCertificate: cfg.GetCertificate, - RootCAs: cfg.RootCAs, - NextProtos: cfg.NextProtos, - ServerName: cfg.ServerName, - ClientAuth: cfg.ClientAuth, - ClientCAs: cfg.ClientCAs, - InsecureSkipVerify: cfg.InsecureSkipVerify, - CipherSuites: cfg.CipherSuites, - PreferServerCipherSuites: cfg.PreferServerCipherSuites, - ClientSessionCache: cfg.ClientSessionCache, - MinVersion: cfg.MinVersion, - MaxVersion: cfg.MaxVersion, - CurvePreferences: cfg.CurvePreferences, - Renegotiation: cfg.Renegotiation, + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, + Renegotiation: cfg.Renegotiation, } } diff --git a/src/net/http/transport_internal_test.go b/src/net/http/transport_internal_test.go new file mode 100644 index 00000000000000..a157d906300a2c --- /dev/null +++ b/src/net/http/transport_internal_test.go @@ -0,0 +1,69 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// White-box tests for transport.go (in package http instead of http_test). + +package http + +import ( + "errors" + "net" + "testing" +) + +// Issue 15446: incorrect wrapping of errors when server closes an idle connection. +func TestTransportPersistConnReadLoopEOF(t *testing.T) { + ln := newLocalListener(t) + defer ln.Close() + + connc := make(chan net.Conn, 1) + go func() { + defer close(connc) + c, err := ln.Accept() + if err != nil { + t.Error(err) + return + } + connc <- c + }() + + tr := new(Transport) + req, _ := NewRequest("GET", "http://"+ln.Addr().String(), nil) + treq := &transportRequest{Request: req} + cm := connectMethod{targetScheme: "http", targetAddr: ln.Addr().String()} + pc, err := tr.getConn(treq, cm) + if err != nil { + t.Fatal(err) + } + defer pc.close(errors.New("test over")) + + conn := <-connc + if conn == nil { + // Already called t.Error in the accept goroutine. + return + } + conn.Close() // simulate the server hanging up on the client + + _, err = pc.roundTrip(treq) + if err != errServerClosedConn && err != errServerClosedIdle { + t.Fatalf("roundTrip = %#v, %v; want errServerClosedConn or errServerClosedIdle", err, err) + } + + <-pc.closech + err = pc.closed + if err != errServerClosedConn && err != errServerClosedIdle { + t.Fatalf("pc.closed = %#v, %v; want errServerClosedConn or errServerClosedIdle", err, err) + } +} + +func newLocalListener(t *testing.T) net.Listener { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + ln, err = net.Listen("tcp6", "[::1]:0") + } + if err != nil { + t.Fatal(err) + } + return ln +} diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index bde052524cb8da..1c1a1d0397234b 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2983,6 +2983,21 @@ func TestTransportAutomaticHTTP2_TLSConfig(t *testing.T) { func TestTransportAutomaticHTTP2_ExpectContinueTimeout(t *testing.T) { testTransportAutoHTTP(t, &Transport{ ExpectContinueTimeout: 1 * time.Second, + }, true) +} + +func TestTransportAutomaticHTTP2_Dial(t *testing.T) { + var d net.Dialer + testTransportAutoHTTP(t, &Transport{ + Dial: d.Dial, + }, false) +} + +func TestTransportAutomaticHTTP2_DialTLS(t *testing.T) { + testTransportAutoHTTP(t, &Transport{ + DialTLS: func(network, addr string) (net.Conn, error) { + panic("unused") + }, }, false) } @@ -3193,26 +3208,33 @@ func TestTransportResponseHeaderLength(t *testing.T) { } } -func TestTransportEventTrace(t *testing.T) { testTransportEventTrace(t, false) } +func TestTransportEventTrace(t *testing.T) { testTransportEventTrace(t, h1Mode, false) } +func TestTransportEventTrace_h2(t *testing.T) { testTransportEventTrace(t, h2Mode, false) } // test a non-nil httptrace.ClientTrace but with all hooks set to zero. -func TestTransportEventTrace_NoHooks(t *testing.T) { testTransportEventTrace(t, true) } +func TestTransportEventTrace_NoHooks(t *testing.T) { testTransportEventTrace(t, h1Mode, true) } +func TestTransportEventTrace_NoHooks_h2(t *testing.T) { testTransportEventTrace(t, h2Mode, true) } -func testTransportEventTrace(t *testing.T, noHooks bool) { +func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { defer afterTest(t) const resBody = "some body" - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + gotWroteReqEvent := make(chan struct{}) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { if _, err := ioutil.ReadAll(r.Body); err != nil { t.Error(err) } + if !noHooks { + select { + case <-gotWroteReqEvent: + case <-time.After(5 * time.Second): + t.Error("timeout waiting for WroteRequest event") + } + } io.WriteString(w, resBody) })) - defer ts.Close() - tr := &Transport{ - ExpectContinueTimeout: 1 * time.Second, - } - defer tr.CloseIdleConnections() - c := &Client{Transport: tr} + defer cst.close() + + cst.tr.ExpectContinueTimeout = 1 * time.Second var mu sync.Mutex var buf bytes.Buffer @@ -3223,7 +3245,8 @@ func testTransportEventTrace(t *testing.T, noHooks bool) { buf.WriteByte('\n') } - ip, port, err := net.SplitHostPort(ts.Listener.Addr().String()) + addrStr := cst.ts.Listener.Addr().String() + ip, port, err := net.SplitHostPort(addrStr) if err != nil { t.Fatal(err) } @@ -3237,7 +3260,7 @@ func testTransportEventTrace(t *testing.T, noHooks bool) { return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil }) - req, _ := NewRequest("POST", "http://dns-is-faked.golang:"+port, strings.NewReader("some body")) + req, _ := NewRequest("POST", cst.scheme()+"://dns-is-faked.golang:"+port, strings.NewReader("some body")) trace := &httptrace.ClientTrace{ GetConn: func(hostPort string) { logf("Getting conn for %v ...", hostPort) }, GotConn: func(ci httptrace.GotConnInfo) { logf("got conn: %+v", ci) }, @@ -3254,7 +3277,10 @@ func testTransportEventTrace(t *testing.T, noHooks bool) { }, Wait100Continue: func() { logf("Wait100Continue") }, Got100Continue: func() { logf("Got100Continue") }, - WroteRequest: func(e httptrace.WroteRequestInfo) { logf("WroteRequest: %+v", e) }, + WroteRequest: func(e httptrace.WroteRequestInfo) { + close(gotWroteReqEvent) + logf("WroteRequest: %+v", e) + }, } if noHooks { // zero out all func pointers, trying to get some path to crash @@ -3263,14 +3289,16 @@ func testTransportEventTrace(t *testing.T, noHooks bool) { req = req.WithContext(httptrace.WithClientTrace(ctx, trace)) req.Header.Set("Expect", "100-continue") - res, err := c.Do(req) + res, err := cst.c.Do(req) if err != nil { t.Fatal(err) } + logf("got roundtrip.response") slurp, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal(err) } + logf("consumed body") if string(slurp) != resBody || res.StatusCode != 200 { t.Fatalf("Got %q, %v; want %q, 200 OK", slurp, res.Status, resBody) } @@ -3289,16 +3317,71 @@ func testTransportEventTrace(t *testing.T, noHooks bool) { t.Errorf("expected substring %q in output.", sub) } } + if strings.Count(got, "got conn: {") != 1 { + t.Errorf("expected exactly 1 \"got conn\" event.") + } wantSub("Getting conn for dns-is-faked.golang:" + port) wantSub("DNS start: {Host:dns-is-faked.golang}") wantSub("DNS done: {Addrs:[{IP:" + ip + " Zone:}] Err: Coalesced:false}") - wantSub("connected to tcp " + ts.Listener.Addr().String() + " = ") + wantSub("Connecting to tcp " + addrStr) + wantSub("connected to tcp " + addrStr + " = ") wantSub("Reused:false WasIdle:false IdleTime:0s") wantSub("first response byte") - wantSub("PutIdleConn = ") - wantSub("WroteRequest: {Err:}") + if !h2 { + wantSub("PutIdleConn = ") + } wantSub("Wait100Continue") wantSub("Got100Continue") + wantSub("WroteRequest: {Err:}") + if strings.Contains(got, " to udp ") { + t.Errorf("should not see UDP (DNS) connections") + } + if t.Failed() { + t.Errorf("Output:\n%s", got) + } +} + +func TestTransportEventTraceRealDNS(t *testing.T) { + defer afterTest(t) + tr := &Transport{} + defer tr.CloseIdleConnections() + c := &Client{Transport: tr} + + var mu sync.Mutex + var buf bytes.Buffer + logf := func(format string, args ...interface{}) { + mu.Lock() + defer mu.Unlock() + fmt.Fprintf(&buf, format, args...) + buf.WriteByte('\n') + } + + req, _ := NewRequest("GET", "http://dns-should-not-resolve.golang:80", nil) + trace := &httptrace.ClientTrace{ + DNSStart: func(e httptrace.DNSStartInfo) { logf("DNSStart: %+v", e) }, + DNSDone: func(e httptrace.DNSDoneInfo) { logf("DNSDone: %+v", e) }, + ConnectStart: func(network, addr string) { logf("ConnectStart: %s %s", network, addr) }, + ConnectDone: func(network, addr string, err error) { logf("ConnectDone: %s %s %v", network, addr, err) }, + } + req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace)) + + resp, err := c.Do(req) + if err == nil { + resp.Body.Close() + t.Fatal("expected error during DNS lookup") + } + + got := buf.String() + wantSub := func(sub string) { + if !strings.Contains(got, sub) { + t.Errorf("expected substring %q in output.", sub) + } + } + wantSub("DNSStart: {Host:dns-should-not-resolve.golang}") + wantSub("DNSDone: {Addrs:[] Err:") + if strings.Contains(got, "ConnectStart") || strings.Contains(got, "ConnectDone") { + t.Errorf("should not see Connect events") + } if t.Failed() { t.Errorf("Output:\n%s", got) } diff --git a/src/net/interface_bsd.go b/src/net/interface_bsd.go index 98d19f2d33d42f..d791cb30167943 100644 --- a/src/net/interface_bsd.go +++ b/src/net/interface_bsd.go @@ -7,74 +7,54 @@ package net import ( - "os" "syscall" - "unsafe" + + "golang.org/x/net/route" ) // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otherwise it returns a mapping of a specific // interface. func interfaceTable(ifindex int) ([]Interface, error) { - tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + msgs, err := interfaceMessages(ifindex) if err != nil { - return nil, os.NewSyscallError("routerib", err) + return nil, err } - msgs, err := syscall.ParseRoutingMessage(tab) - if err != nil { - return nil, os.NewSyscallError("parseroutingmessage", err) + n := len(msgs) + if ifindex != 0 { + n = 1 } - return parseInterfaceTable(ifindex, msgs) -} - -func parseInterfaceTable(ifindex int, msgs []syscall.RoutingMessage) ([]Interface, error) { - var ift []Interface -loop: + ift := make([]Interface, n) + n = 0 for _, m := range msgs { switch m := m.(type) { - case *syscall.InterfaceMessage: - if ifindex == 0 || ifindex == int(m.Header.Index) { - ifi, err := newLink(m) - if err != nil { - return nil, err - } - ift = append(ift, *ifi) - if ifindex == int(m.Header.Index) { - break loop + case *route.InterfaceMessage: + if ifindex != 0 && ifindex != m.Index { + continue + } + ift[n].Index = m.Index + ift[n].Name = m.Name + ift[n].Flags = linkFlags(m.Flags) + if sa, ok := m.Addrs[syscall.RTAX_IFP].(*route.LinkAddr); ok && len(sa.Addr) > 0 { + ift[n].HardwareAddr = make([]byte, len(sa.Addr)) + copy(ift[n].HardwareAddr, sa.Addr) + } + for _, sys := range m.Sys() { + if imx, ok := sys.(*route.InterfaceMetrics); ok { + ift[n].MTU = imx.MTU + break } } + n++ + if ifindex == m.Index { + return ift[:n], nil + } } } - return ift, nil -} - -func newLink(m *syscall.InterfaceMessage) (*Interface, error) { - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, os.NewSyscallError("parseroutingsockaddr", err) - } - ifi := &Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)} - sa, _ := sas[syscall.RTAX_IFP].(*syscall.SockaddrDatalink) - if sa != nil { - // NOTE: SockaddrDatalink.Data is minimum work area, - // can be larger. - m.Data = m.Data[unsafe.Offsetof(sa.Data):] - var name [syscall.IFNAMSIZ]byte - for i := 0; i < int(sa.Nlen); i++ { - name[i] = m.Data[i] - } - ifi.Name = string(name[:sa.Nlen]) - ifi.MTU = int(m.Header.Data.Mtu) - addr := make([]byte, sa.Alen) - for i := 0; i < int(sa.Alen); i++ { - addr[i] = m.Data[int(sa.Nlen)+i] - } - ifi.HardwareAddr = addr[:sa.Alen] - } - return ifi, nil + return ift[:n], nil } -func linkFlags(rawFlags int32) Flags { +func linkFlags(rawFlags int) Flags { var f Flags if rawFlags&syscall.IFF_UP != 0 { f |= FlagUp @@ -102,75 +82,37 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { if ifi != nil { index = ifi.Index } - tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, index) - if err != nil { - return nil, os.NewSyscallError("routerib", err) - } - msgs, err := syscall.ParseRoutingMessage(tab) + msgs, err := interfaceMessages(index) if err != nil { - return nil, os.NewSyscallError("parseroutingmessage", err) + return nil, err } - var ift []Interface - if index == 0 { - ift, err = parseInterfaceTable(index, msgs) - if err != nil { - return nil, err - } - } - var ifat []Addr + ifat := make([]Addr, 0, len(msgs)) for _, m := range msgs { switch m := m.(type) { - case *syscall.InterfaceAddrMessage: - if index == 0 || index == int(m.Header.Index) { - if index == 0 { - var err error - ifi, err = interfaceByIndex(ift, int(m.Header.Index)) - if err != nil { - return nil, err - } - } - ifa, err := newAddr(ifi, m) - if err != nil { - return nil, err - } - if ifa != nil { - ifat = append(ifat, ifa) - } + case *route.InterfaceAddrMessage: + if index != 0 && index != m.Index { + continue + } + var mask IPMask + switch sa := m.Addrs[syscall.RTAX_NETMASK].(type) { + case *route.Inet4Addr: + mask = IPv4Mask(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) + case *route.Inet6Addr: + mask = make(IPMask, IPv6len) + copy(mask, sa.IP[:]) + } + var ip IP + switch sa := m.Addrs[syscall.RTAX_IFA].(type) { + case *route.Inet4Addr: + ip = IPv4(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) + case *route.Inet6Addr: + ip = make(IP, IPv6len) + copy(ip, sa.IP[:]) + } + if ip != nil && mask != nil { // NetBSD may contain route.LinkAddr + ifat = append(ifat, &IPNet{IP: ip, Mask: mask}) } } } return ifat, nil } - -func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) { - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, os.NewSyscallError("parseroutingsockaddr", err) - } - ifa := &IPNet{} - switch sa := sas[syscall.RTAX_NETMASK].(type) { - case *syscall.SockaddrInet4: - ifa.Mask = IPv4Mask(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) - case *syscall.SockaddrInet6: - ifa.Mask = make(IPMask, IPv6len) - copy(ifa.Mask, sa.Addr[:]) - } - switch sa := sas[syscall.RTAX_IFA].(type) { - case *syscall.SockaddrInet4: - ifa.IP = IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) - case *syscall.SockaddrInet6: - ifa.IP = make(IP, IPv6len) - copy(ifa.IP, sa.Addr[:]) - // NOTE: KAME based IPv6 protocol stack usually embeds - // the interface index in the interface-local or - // link-local address as the kernel-internal form. - if ifa.IP.IsLinkLocalUnicast() { - ifa.IP[2], ifa.IP[3] = 0, 0 - ifa.Zone = ifi.Name - } - } - if ifa.IP == nil || ifa.Mask == nil { - return nil, nil // Sockaddrs contain syscall.SockaddrDatalink on NetBSD - } - return ifa, nil -} diff --git a/src/net/interface_bsd_test.go b/src/net/interface_bsd_test.go index ed1af554ad4294..69b0fbcab3de30 100644 --- a/src/net/interface_bsd_test.go +++ b/src/net/interface_bsd_test.go @@ -9,10 +9,15 @@ package net import ( "fmt" "os/exec" + "runtime" ) -func (ti *testInterface) setBroadcast(suffix int) error { - ti.name = fmt.Sprintf("vlan%d", suffix) +func (ti *testInterface) setBroadcast(vid int) error { + if runtime.GOOS == "openbsd" { + ti.name = fmt.Sprintf("vether%d", vid) + } else { + ti.name = fmt.Sprintf("vlan%d", vid) + } xname, err := exec.LookPath("ifconfig") if err != nil { return err diff --git a/src/net/interface_netbsd.go b/src/net/interface_bsdvar.go similarity index 54% rename from src/net/interface_netbsd.go rename to src/net/interface_bsdvar.go index cb7a34ab166004..a809b5f5ce49e6 100644 --- a/src/net/interface_netbsd.go +++ b/src/net/interface_bsdvar.go @@ -2,8 +2,24 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build dragonfly netbsd openbsd + package net +import ( + "syscall" + + "golang.org/x/net/route" +) + +func interfaceMessages(ifindex int) ([]route.Message, error) { + rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLIST, ifindex) + if err != nil { + return nil, err + } + return route.ParseRIB(syscall.NET_RT_IFLIST, rib) +} + // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { diff --git a/src/net/interface_darwin.go b/src/net/interface_darwin.go index 72fb9443c07d49..bb4fd73a987670 100644 --- a/src/net/interface_darwin.go +++ b/src/net/interface_darwin.go @@ -5,58 +5,49 @@ package net import ( - "os" "syscall" + + "golang.org/x/net/route" ) +func interfaceMessages(ifindex int) ([]route.Message, error) { + rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLIST, ifindex) + if err != nil { + return nil, err + } + return route.ParseRIB(syscall.NET_RT_IFLIST, rib) +} + // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifi.Index) + rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLIST2, ifi.Index) if err != nil { - return nil, os.NewSyscallError("routerib", err) + return nil, err } - msgs, err := syscall.ParseRoutingMessage(tab) + msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib) if err != nil { - return nil, os.NewSyscallError("parseroutingmessage", err) + return nil, err } - var ifmat []Addr + ifmat := make([]Addr, 0, len(msgs)) for _, m := range msgs { switch m := m.(type) { - case *syscall.InterfaceMulticastAddrMessage: - if ifi.Index == int(m.Header.Index) { - ifma, err := newMulticastAddr(ifi, m) - if err != nil { - return nil, err - } - if ifma != nil { - ifmat = append(ifmat, ifma) - } + case *route.InterfaceMulticastAddrMessage: + if ifi.Index != m.Index { + continue + } + var ip IP + switch sa := m.Addrs[syscall.RTAX_IFA].(type) { + case *route.Inet4Addr: + ip = IPv4(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) + case *route.Inet6Addr: + ip = make(IP, IPv6len) + copy(ip, sa.IP[:]) + } + if ip != nil { + ifmat = append(ifmat, &IPAddr{IP: ip}) } } } return ifmat, nil } - -func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) (*IPAddr, error) { - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, os.NewSyscallError("parseroutingsockaddr", err) - } - switch sa := sas[syscall.RTAX_IFA].(type) { - case *syscall.SockaddrInet4: - return &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])}, nil - case *syscall.SockaddrInet6: - ifma := IPAddr{IP: make(IP, IPv6len)} - copy(ifma.IP, sa.Addr[:]) - // NOTE: KAME based IPv6 protocol stack usually embeds - // the interface index in the interface-local or - // link-local address as the kernel-internal form. - if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { - ifma.IP[2], ifma.IP[3] = 0, 0 - } - return &ifma, nil - default: - return nil, nil - } -} diff --git a/src/net/interface_dragonfly.go b/src/net/interface_dragonfly.go deleted file mode 100644 index cb7a34ab166004..00000000000000 --- a/src/net/interface_dragonfly.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package net - -// interfaceMulticastAddrTable returns addresses for a specific -// interface. -func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - // TODO(mikio): Implement this like other platforms. - return nil, nil -} diff --git a/src/net/interface_freebsd.go b/src/net/interface_freebsd.go index bddee8bacb75f2..45badd64954a17 100644 --- a/src/net/interface_freebsd.go +++ b/src/net/interface_freebsd.go @@ -5,58 +5,54 @@ package net import ( - "os" "syscall" + + "golang.org/x/net/route" ) +func interfaceMessages(ifindex int) ([]route.Message, error) { + typ := route.RIBType(syscall.NET_RT_IFLISTL) + rib, err := route.FetchRIB(syscall.AF_UNSPEC, typ, ifindex) + if err != nil { + typ = route.RIBType(syscall.NET_RT_IFLIST) + rib, err = route.FetchRIB(syscall.AF_UNSPEC, typ, ifindex) + } + if err != nil { + return nil, err + } + return route.ParseRIB(typ, rib) +} + // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifi.Index) + rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFMALIST, ifi.Index) if err != nil { - return nil, os.NewSyscallError("routerib", err) + return nil, err } - msgs, err := syscall.ParseRoutingMessage(tab) + msgs, err := route.ParseRIB(syscall.NET_RT_IFMALIST, rib) if err != nil { - return nil, os.NewSyscallError("parseroutingmessage", err) + return nil, err } - var ifmat []Addr + ifmat := make([]Addr, 0, len(msgs)) for _, m := range msgs { switch m := m.(type) { - case *syscall.InterfaceMulticastAddrMessage: - if ifi.Index == int(m.Header.Index) { - ifma, err := newMulticastAddr(ifi, m) - if err != nil { - return nil, err - } - if ifma != nil { - ifmat = append(ifmat, ifma) - } + case *route.InterfaceMulticastAddrMessage: + if ifi.Index != m.Index { + continue + } + var ip IP + switch sa := m.Addrs[syscall.RTAX_IFA].(type) { + case *route.Inet4Addr: + ip = IPv4(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) + case *route.Inet6Addr: + ip = make(IP, IPv6len) + copy(ip, sa.IP[:]) + } + if ip != nil { + ifmat = append(ifmat, &IPAddr{IP: ip}) } } } return ifmat, nil } - -func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) (*IPAddr, error) { - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, os.NewSyscallError("parseroutingsockaddr", err) - } - switch sa := sas[syscall.RTAX_IFA].(type) { - case *syscall.SockaddrInet4: - return &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])}, nil - case *syscall.SockaddrInet6: - ifma := IPAddr{IP: make(IP, IPv6len)} - copy(ifma.IP, sa.Addr[:]) - // NOTE: KAME based IPv6 protocol stack usually embeds - // the interface index in the interface-local or - // link-local address as the kernel-internal form. - if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { - ifma.IP[2], ifma.IP[3] = 0, 0 - } - return &ifma, nil - default: - return nil, nil - } -} diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go index b8f57fd7db39f0..5e391b28b0f8c4 100644 --- a/src/net/interface_linux.go +++ b/src/net/interface_linux.go @@ -193,9 +193,6 @@ func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRou case syscall.AF_INET6: ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)} copy(ifa.IP, a.Value[:]) - if ifa.IP.IsLinkLocalUnicast() { - ifa.Zone = ifi.Name - } return ifa } } diff --git a/src/net/interface_openbsd.go b/src/net/interface_openbsd.go deleted file mode 100644 index cb7a34ab166004..00000000000000 --- a/src/net/interface_openbsd.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package net - -// interfaceMulticastAddrTable returns addresses for a specific -// interface. -func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - // TODO(mikio): Implement this like other platforms. - return nil, nil -} diff --git a/src/net/interface_test.go b/src/net/interface_test.go index c3e1ee231ffce9..4c695b902a226c 100644 --- a/src/net/interface_test.go +++ b/src/net/interface_test.go @@ -5,7 +5,7 @@ package net import ( - "internal/testenv" + "fmt" "reflect" "runtime" "testing" @@ -48,24 +48,11 @@ func ipv6LinkLocalUnicastAddr(ifi *Interface) string { return "" } -type routeStats struct { - loop int // # of active loopback interfaces - other int // # of active other interfaces - - uni4, uni6 int // # of active connected unicast, anycast routes - multi4, multi6 int // # of active connected multicast route clones -} - func TestInterfaces(t *testing.T) { - if runtime.GOOS == "freebsd" && runtime.GOARCH == "arm" { - // 100% flaky, actually, at least on some FreeBSD versions - testenv.SkipFlaky(t, 15262) - } ift, err := Interfaces() if err != nil { t.Fatal(err) } - var stats routeStats for _, ifi := range ift { ifxi, err := InterfaceByIndex(ifi.Index) if err != nil { @@ -81,56 +68,7 @@ func TestInterfaces(t *testing.T) { if !reflect.DeepEqual(ifxn, &ifi) { t.Errorf("got %v; want %v", ifxn, ifi) } - t.Logf("%q: flags %q, ifindex %v, mtu %v", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU) - t.Logf("hardware address %q", ifi.HardwareAddr.String()) - if ifi.Flags&FlagUp != 0 { - if ifi.Flags&FlagLoopback != 0 { - stats.loop++ - } else { - stats.other++ - } - } - n4, n6 := testInterfaceAddrs(t, &ifi) - stats.uni4 += n4 - stats.uni6 += n6 - n4, n6 = testInterfaceMulticastAddrs(t, &ifi) - stats.multi4 += n4 - stats.multi6 += n6 - } - switch runtime.GOOS { - case "nacl", "plan9", "solaris": - default: - // Test the existence of connected unicast routes for - // IPv4. - if supportsIPv4 && stats.loop+stats.other > 0 && stats.uni4 == 0 { - t.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v", stats) - } - // Test the existence of connected unicast routes for - // IPv6. We can assume the existence of ::1/128 when - // at least one loopback interface is installed. - if supportsIPv6 && stats.loop > 0 && stats.uni6 == 0 { - t.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v", stats) - } - } - switch runtime.GOOS { - case "dragonfly", "nacl", "netbsd", "openbsd", "plan9", "solaris": - default: - // Test the existence of connected multicast route - // clones for IPv4. Unlike IPv6, IPv4 multicast - // capability is not a mandatory feature, and so this - // test is disabled. - //if supportsIPv4 && stats.loop > 0 && stats.uni4 > 1 && stats.multi4 == 0 { - // t.Errorf("num IPv4 multicast route clones = 0; want >0; summary: %+v", stats) - //} - // Test the existence of connected multicast route - // clones for IPv6. Some platform never uses loopback - // interface as the nexthop for multicast routing. - // We can assume the existence of connected multicast - // route clones when at least two connected unicast - // routes, ::1/128 and other, are installed. - if supportsIPv6 && stats.loop > 0 && stats.uni6 > 1 && stats.multi6 == 0 { - t.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v", stats) - } + t.Logf("%s: flags=%v index=%d mtu=%d hwaddr=%v", ifi.Name, ifi.Flags, ifi.Index, ifi.MTU, ifi.HardwareAddr) } } @@ -139,142 +77,217 @@ func TestInterfaceAddrs(t *testing.T) { if err != nil { t.Fatal(err) } - var stats routeStats - for _, ifi := range ift { - if ifi.Flags&FlagUp != 0 { - if ifi.Flags&FlagLoopback != 0 { - stats.loop++ - } else { - stats.other++ - } - } - } + ifStats := interfaceStats(ift) ifat, err := InterfaceAddrs() if err != nil { t.Fatal(err) } - stats.uni4, stats.uni6 = testAddrs(t, ifat) - // Test the existence of connected unicast routes for IPv4. - if supportsIPv4 && stats.loop+stats.other > 0 && stats.uni4 == 0 { - t.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v", stats) + uniStats, err := validateInterfaceUnicastAddrs(ifat) + if err != nil { + t.Fatal(err) } - // Test the existence of connected unicast routes for IPv6. - // We can assume the existence of ::1/128 when at least one - // loopback interface is installed. - if supportsIPv6 && stats.loop > 0 && stats.uni6 == 0 { - t.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v", stats) + if err := checkUnicastStats(ifStats, uniStats); err != nil { + t.Fatal(err) } } -func testInterfaceAddrs(t *testing.T, ifi *Interface) (naf4, naf6 int) { - ifat, err := ifi.Addrs() +func TestInterfaceUnicastAddrs(t *testing.T) { + ift, err := Interfaces() + if err != nil { + t.Fatal(err) + } + ifStats := interfaceStats(ift) if err != nil { t.Fatal(err) } - return testAddrs(t, ifat) + var uniStats routeStats + for _, ifi := range ift { + ifat, err := ifi.Addrs() + if err != nil { + t.Fatal(ifi, err) + } + stats, err := validateInterfaceUnicastAddrs(ifat) + if err != nil { + t.Fatal(ifi, err) + } + uniStats.ipv4 += stats.ipv4 + uniStats.ipv6 += stats.ipv6 + } + if err := checkUnicastStats(ifStats, &uniStats); err != nil { + t.Fatal(err) + } } -func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) (nmaf4, nmaf6 int) { - ifmat, err := ifi.MulticastAddrs() +func TestInterfaceMulticastAddrs(t *testing.T) { + ift, err := Interfaces() + if err != nil { + t.Fatal(err) + } + ifStats := interfaceStats(ift) + ifat, err := InterfaceAddrs() + if err != nil { + t.Fatal(err) + } + uniStats, err := validateInterfaceUnicastAddrs(ifat) if err != nil { t.Fatal(err) } - return testMulticastAddrs(t, ifmat) + var multiStats routeStats + for _, ifi := range ift { + ifmat, err := ifi.MulticastAddrs() + if err != nil { + t.Fatal(ifi, err) + } + stats, err := validateInterfaceMulticastAddrs(ifmat) + if err != nil { + t.Fatal(ifi, err) + } + multiStats.ipv4 += stats.ipv4 + multiStats.ipv6 += stats.ipv6 + } + if err := checkMulticastStats(ifStats, uniStats, &multiStats); err != nil { + t.Fatal(err) + } +} + +type ifStats struct { + loop int // # of active loopback interfaces + other int // # of active other interfaces +} + +func interfaceStats(ift []Interface) *ifStats { + var stats ifStats + for _, ifi := range ift { + if ifi.Flags&FlagUp != 0 { + if ifi.Flags&FlagLoopback != 0 { + stats.loop++ + } else { + stats.other++ + } + } + } + return &stats +} + +type routeStats struct { + ipv4, ipv6 int // # of active connected unicast, anycast or multicast routes } -func testAddrs(t *testing.T, ifat []Addr) (naf4, naf6 int) { +func validateInterfaceUnicastAddrs(ifat []Addr) (*routeStats, error) { // Note: BSD variants allow assigning any IPv4/IPv6 address // prefix to IP interface. For example, // - 0.0.0.0/0 through 255.255.255.255/32 // - ::/0 through ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 // In other words, there is no tightly-coupled combination of // interface address prefixes and connected routes. + stats := new(routeStats) for _, ifa := range ifat { switch ifa := ifa.(type) { case *IPNet: if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() || ifa.Mask == nil { - t.Errorf("unexpected value: %#v", ifa) - continue + return nil, fmt.Errorf("unexpected value: %#v", ifa) } if len(ifa.IP) != IPv6len { - t.Errorf("should be internal representation either IPv6 or IPv6 IPv4-mapped address: %#v", ifa) - continue + return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa) } prefixLen, maxPrefixLen := ifa.Mask.Size() if ifa.IP.To4() != nil { if 0 >= prefixLen || prefixLen > 8*IPv4len || maxPrefixLen != 8*IPv4len { - t.Errorf("unexpected prefix length: %d/%d", prefixLen, maxPrefixLen) - continue + return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) } if ifa.IP.IsLoopback() && (prefixLen != 8 && prefixLen != 8*IPv4len) { // see RFC 1122 - t.Errorf("unexpected prefix length for IPv4 loopback: %d/%d", prefixLen, maxPrefixLen) - continue + return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) } - naf4++ + stats.ipv4++ } if ifa.IP.To16() != nil && ifa.IP.To4() == nil { if 0 >= prefixLen || prefixLen > 8*IPv6len || maxPrefixLen != 8*IPv6len { - t.Errorf("unexpected prefix length: %d/%d", prefixLen, maxPrefixLen) - continue + return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) } if ifa.IP.IsLoopback() && prefixLen != 8*IPv6len { // see RFC 4291 - t.Errorf("unexpected prefix length for IPv6 loopback: %d/%d", prefixLen, maxPrefixLen) - continue + return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa) } - if ifa.IP.IsLinkLocalUnicast() && ifa.Zone == "" { - t.Errorf("no IPv6 zone identifier found: %#v", ifa) - continue - } - naf6++ + stats.ipv6++ } - t.Logf("interface address %q", ifa.String()) case *IPAddr: if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() { - t.Errorf("unexpected value: %#v", ifa) - continue + return nil, fmt.Errorf("unexpected value: %#v", ifa) } if len(ifa.IP) != IPv6len { - t.Errorf("should be internal representation either IPv6 or IPv6 IPv4-mapped address: %#v", ifa) - continue + return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa) } if ifa.IP.To4() != nil { - naf4++ + stats.ipv4++ } if ifa.IP.To16() != nil && ifa.IP.To4() == nil { - naf6++ + stats.ipv6++ } - t.Logf("interface address %q", ifa.String()) default: - t.Errorf("unexpected type: %T", ifa) + return nil, fmt.Errorf("unexpected type: %T", ifa) } } - return + return stats, nil } -func testMulticastAddrs(t *testing.T, ifmat []Addr) (nmaf4, nmaf6 int) { - for _, ifma := range ifmat { - switch ifma := ifma.(type) { +func validateInterfaceMulticastAddrs(ifat []Addr) (*routeStats, error) { + stats := new(routeStats) + for _, ifa := range ifat { + switch ifa := ifa.(type) { case *IPAddr: - if ifma == nil || ifma.IP == nil || ifma.IP.IsUnspecified() || !ifma.IP.IsMulticast() { - t.Errorf("unexpected value: %+v", ifma) - continue + if ifa == nil || ifa.IP == nil || ifa.IP.IsUnspecified() || !ifa.IP.IsMulticast() { + return nil, fmt.Errorf("unexpected value: %#v", ifa) } - if len(ifma.IP) != IPv6len { - t.Errorf("should be internal representation either IPv6 or IPv6 IPv4-mapped address: %#v", ifma) - continue + if len(ifa.IP) != IPv6len { + return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa) } - if ifma.IP.To4() != nil { - nmaf4++ + if ifa.IP.To4() != nil { + stats.ipv4++ } - if ifma.IP.To16() != nil && ifma.IP.To4() == nil { - nmaf6++ + if ifa.IP.To16() != nil && ifa.IP.To4() == nil { + stats.ipv6++ } - t.Logf("joined group address %q", ifma.String()) default: - t.Errorf("unexpected type: %T", ifma) + return nil, fmt.Errorf("unexpected type: %T", ifa) } } - return + return stats, nil +} + +func checkUnicastStats(ifStats *ifStats, uniStats *routeStats) error { + // Test the existence of connected unicast routes for IPv4. + if supportsIPv4 && ifStats.loop+ifStats.other > 0 && uniStats.ipv4 == 0 { + return fmt.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats) + } + // Test the existence of connected unicast routes for IPv6. + // We can assume the existence of ::1/128 when at least one + // loopback interface is installed. + if supportsIPv6 && ifStats.loop > 0 && uniStats.ipv6 == 0 { + return fmt.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats) + } + return nil +} + +func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) error { + switch runtime.GOOS { + case "dragonfly", "nacl", "netbsd", "openbsd", "plan9", "solaris": + default: + // Test the existence of connected multicast route + // clones for IPv4. Unlike IPv6, IPv4 multicast + // capability is not a mandatory feature, and so IPv4 + // multicast validation is ignored and we only check + // IPv6 below. + // + // Test the existence of connected multicast route + // clones for IPv6. Some platform never uses loopback + // interface as the nexthop for multicast routing. + // We can assume the existence of connected multicast + // route clones when at least two connected unicast + // routes, ::1/128 and other, are installed. + if supportsIPv6 && ifStats.loop > 0 && uniStats.ipv6 > 1 && multiStats.ipv6 == 0 { + return fmt.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v, %+v, %+v", ifStats, uniStats, multiStats) + } + } + return nil } func BenchmarkInterfaces(b *testing.B) { diff --git a/src/net/interface_unix_test.go b/src/net/interface_unix_test.go index 2ebf0897360be9..36510ebf080fbf 100644 --- a/src/net/interface_unix_test.go +++ b/src/net/interface_unix_test.go @@ -7,6 +7,7 @@ package net import ( + "fmt" "os" "os/exec" "runtime" @@ -24,8 +25,8 @@ type testInterface struct { func (ti *testInterface) setup() error { for _, cmd := range ti.setupCmds { - if err := cmd.Run(); err != nil { - return err + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("args=%v out=%q err=%v", cmd.Args, string(out), err) } } return nil @@ -33,8 +34,8 @@ func (ti *testInterface) setup() error { func (ti *testInterface) teardown() error { for _, cmd := range ti.teardownCmds { - if err := cmd.Run(); err != nil { - return err + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("args=%v out=%q err=%v ", cmd.Args, string(out), err) } } return nil @@ -51,6 +52,8 @@ func TestPointToPointInterface(t *testing.T) { t.Skip("must be root") } + // We suppose that using IPv4 link-local addresses doesn't + // harm anyone. local, remote := "169.254.0.1", "169.254.0.254" ip := ParseIP(remote) for i := 0; i < 3; i++ { @@ -100,15 +103,17 @@ func TestInterfaceArrivalAndDeparture(t *testing.T) { t.Skip("must be root") } + // We suppose that using IPv4 link-local addresses and the + // dot1Q ID for Token Ring and FDDI doesn't harm anyone. local, remote := "169.254.0.1", "169.254.0.254" ip := ParseIP(remote) - for i := 0; i < 3; i++ { + for _, vid := range []int{1002, 1003, 1004, 1005} { ift1, err := Interfaces() if err != nil { t.Fatal(err) } ti := &testInterface{local: local, remote: remote} - if err := ti.setBroadcast(5682 + i); err != nil { + if err := ti.setBroadcast(vid); err != nil { t.Skipf("test requires external command: %v", err) } if err := ti.setup(); err != nil { diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go index 69de095e667d8c..8b976e585f36aa 100644 --- a/src/net/interface_windows.go +++ b/src/net/interface_windows.go @@ -159,9 +159,6 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { } ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(l, 8*IPv6len)} copy(ifa.IP, sa.Addr[:]) - if ifa.IP.IsLinkLocalUnicast() { - ifa.Zone = syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(aa.FriendlyName)))[:]) - } ifat = append(ifat, ifa) } } diff --git a/src/net/ip.go b/src/net/ip.go index e8b0fd990bf896..d0c82630b57830 100644 --- a/src/net/ip.go +++ b/src/net/ip.go @@ -36,7 +36,6 @@ type IPMask []byte type IPNet struct { IP IP // network number Mask IPMask // network mask - Zone string // IPv6 scoped addressing zone } // IPv4 returns the IP address (in 16-byte form) of the @@ -256,7 +255,7 @@ func (ip IP) Mask(mask IPMask) IP { // It returns one of 4 forms: // - "", if ip has length 0 // - dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address -// - IPv6 ("2001:db9::1"), if ip is a valid IPv6 address +// - IPv6 ("2001:db8::1"), if ip is a valid IPv6 address // - the hexadecimal form of ip, without punctuation, if no other cases apply func (ip IP) String() string { p := ip @@ -273,7 +272,7 @@ func (ip IP) String() string { uitoa(uint(p4[3])) } if len(p) != IPv6len { - return hexString(ip) + return "?" + hexString(ip) } // Find longest run of zeros. @@ -339,7 +338,7 @@ func (ip IP) MarshalText() ([]byte, error) { return []byte(""), nil } if len(ip) != IPv4len && len(ip) != IPv6len { - return nil, &AddrError{Err: "invalid IP address", Addr: ip.String()} + return nil, &AddrError{Err: "invalid IP address", Addr: hexString(ip)} } return []byte(ip.String()), nil } @@ -484,26 +483,22 @@ func (n *IPNet) Contains(ip IP) bool { // Network returns the address's network name, "ip+net". func (n *IPNet) Network() string { return "ip+net" } -// String returns the CIDR notation of n like "192.168.100.1/24" -// or "2001:DB8::/48" as defined in RFC 4632 and RFC 4291. +// String returns the CIDR notation of n like "192.0.2.1/24" +// or "2001:db8::/48" as defined in RFC 4632 and RFC 4291. // If the mask is not in the canonical form, it returns the // string which consists of an IP address, followed by a slash // character and a mask expressed as hexadecimal form with no -// punctuation like "192.168.100.1/c000ff00". +// punctuation like "198.51.100.1/c000ff00". func (n *IPNet) String() string { nn, m := networkNumberAndMask(n) if nn == nil || m == nil { return "" } - ip := nn.String() - if n.Zone != "" { - ip = ip + "%" + n.Zone - } l := simpleMaskLength(m) if l == -1 { - return ip + "/" + m.String() + return nn.String() + "/" + m.String() } - return ip + "/" + uitoa(uint(l)) + return nn.String() + "/" + uitoa(uint(l)) } // Parse IPv4 address (d.d.d.d). @@ -646,8 +641,8 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) { } // ParseIP parses s as an IP address, returning the result. -// The string s can be in dotted decimal ("74.125.19.99") -// or IPv6 ("2001:4860:0:2001::68") form. +// The string s can be in dotted decimal ("192.0.2.1") +// or IPv6 ("2001:db8::68") form. // If s is not a valid textual representation of an IP address, // ParseIP returns nil. func ParseIP(s string) IP { @@ -664,29 +659,28 @@ func ParseIP(s string) IP { } // ParseCIDR parses s as a CIDR notation IP address and mask, -// like "192.168.100.1/24" or "2001:DB8::/48", as defined in +// like "192.0.2.0/24" or "2001:db8::/32", as defined in // RFC 4632 and RFC 4291. // // It returns the IP address and the network implied by the IP -// and mask. For example, ParseCIDR("192.168.100.1/16") returns -// the IP address 192.168.100.1 and the network 192.168.0.0/16. +// and mask. For example, ParseCIDR("198.51.100.1/24") returns +// the IP address 198.51.100.1 and the network 198.51.100.0/24. func ParseCIDR(s string) (IP, *IPNet, error) { i := byteIndex(s, '/') if i < 0 { return nil, nil, &ParseError{Type: "CIDR address", Text: s} } - var zone string addr, mask := s[:i], s[i+1:] iplen := IPv4len ip := parseIPv4(addr) if ip == nil { iplen = IPv6len - ip, zone = parseIPv6(addr, true) + ip, _ = parseIPv6(addr, false) } n, i, ok := dtoi(mask, 0) if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen { return nil, nil, &ParseError{Type: "CIDR address", Text: s} } m := CIDRMask(n, 8*iplen) - return ip, &IPNet{IP: ip.Mask(m), Mask: m, Zone: zone}, nil + return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil } diff --git a/src/net/ip_test.go b/src/net/ip_test.go index 1d67057d6afbbc..b6ac26da05b84a 100644 --- a/src/net/ip_test.go +++ b/src/net/ip_test.go @@ -225,7 +225,7 @@ var ipStringTests = []struct { // Opaque byte sequence { IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, - "0123456789abcdef", + "?0123456789abcdef", nil, &AddrError{Err: "invalid IP address", Addr: "0123456789abcdef"}, }, @@ -327,9 +327,6 @@ var parseCIDRTests = []struct { {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil}, {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, - {"fe80::%en0/64", ParseIP("fe80::"), &IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, nil}, - {"fe80::1%en0/64", ParseIP("fe80::1"), &IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, nil}, - {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/255.255.255.0"}}, {"192.168.1.1/35", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/35"}}, {"2001:db8::1/-1", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-1"}}, @@ -376,13 +373,8 @@ var ipNetStringTests = []struct { out string }{ {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"}, - {&IPNet{IP: IPv4(192, 168, 1, 1), Mask: CIDRMask(26, 32)}, "192.168.1.1/26"}, {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, - {&IPNet{IP: ParseIP("fe80::"), Mask: CIDRMask(64, 128), Zone: "en0"}, "fe80::%en0/64"}, - {&IPNet{IP: ParseIP("fe80::1"), Mask: CIDRMask(64, 128), Zone: "en0"}, "fe80::1%en0/64"}, - {&IPNet{IP: ParseIP("fe80::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::")), Zone: "en0"}, "fe80::%en0/8000f1230000cafe0000000000000000"}, {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"}, - {&IPNet{IP: ParseIP("2001:db8::1"), Mask: CIDRMask(55, 128)}, "2001:db8::1/55"}, {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, } diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go index 73147a2d3f7c40..3f7af2a1747c32 100644 --- a/src/net/lookup_plan9.go +++ b/src/net/lookup_plan9.go @@ -7,6 +7,7 @@ package net import ( "context" "errors" + "io" "os" ) @@ -17,7 +18,7 @@ func query(ctx context.Context, filename, query string, bufSize int) (res []stri } defer file.Close() - _, err = file.Seek(0, 0) + _, err = file.Seek(0, io.SeekStart) if err != nil { return } @@ -25,7 +26,7 @@ func query(ctx context.Context, filename, query string, bufSize int) (res []stri if err != nil { return } - _, err = file.Seek(0, 0) + _, err = file.Seek(0, io.SeekStart) if err != nil { return } diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go index 6e54fdba76d902..e22d1fbf79b42f 100644 --- a/src/net/lookup_test.go +++ b/src/net/lookup_test.go @@ -70,6 +70,7 @@ func TestLookupGoogleSRV(t *testing.T) { for _, tt := range lookupGoogleSRVTests { cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name) if err != nil { + testenv.SkipFlakyNet(t) t.Fatal(err) } if len(srvs) == 0 { @@ -137,6 +138,7 @@ func TestLookupGmailNS(t *testing.T) { for _, tt := range lookupGmailNSTests { nss, err := LookupNS(tt.name) if err != nil { + testenv.SkipFlakyNet(t) t.Fatal(err) } if len(nss) == 0 { @@ -369,12 +371,23 @@ func TestReverseAddress(t *testing.T) { } } -func TestLookupIPDeadline(t *testing.T) { +func TestDNSFlood(t *testing.T) { if !*testDNSFlood { t.Skip("test disabled; use -dnsflood to enable") } - const N = 5000 + var N = 5000 + if runtime.GOOS == "darwin" { + // On Darwin this test consumes kernel threads much + // than other platforms for some reason. + // When we monitor the number of allocated Ms by + // observing on runtime.newm calls, we can see that it + // easily reaches the per process ceiling + // kern.num_threads when CGO_ENABLED=1 and + // GODEBUG=netdns=go. + N = 500 + } + const timeout = 3 * time.Second ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2) defer cancel() @@ -498,6 +511,7 @@ func TestLookupDotsWithRemoteSource(t *testing.T) { func testDots(t *testing.T, mode string) { names, err := LookupAddr("8.8.8.8") // Google dns server if err != nil { + testenv.SkipFlakyNet(t) t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode) } else { for _, name := range names { @@ -509,12 +523,16 @@ func testDots(t *testing.T, mode string) { } cname, err := LookupCNAME("www.mit.edu") - if err != nil || !strings.HasSuffix(cname, ".") { - t.Errorf("LookupCNAME(www.mit.edu) = %v, %v, want cname ending in . with trailing dot (mode=%v)", cname, err, mode) + if err != nil { + testenv.SkipFlakyNet(t) + t.Errorf("LookupCNAME(www.mit.edu, mode=%v): %v", mode, err) + } else if !strings.HasSuffix(cname, ".") { + t.Errorf("LookupCNAME(www.mit.edu) = %v, want cname ending in . with trailing dot (mode=%v)", cname, mode) } mxs, err := LookupMX("google.com") if err != nil { + testenv.SkipFlakyNet(t) t.Errorf("LookupMX(google.com): %v (mode=%v)", err, mode) } else { for _, mx := range mxs { @@ -527,6 +545,7 @@ func testDots(t *testing.T, mode string) { nss, err := LookupNS("google.com") if err != nil { + testenv.SkipFlakyNet(t) t.Errorf("LookupNS(google.com): %v (mode=%v)", err, mode) } else { for _, ns := range nss { @@ -539,6 +558,7 @@ func testDots(t *testing.T, mode string) { cname, srvs, err := LookupSRV("xmpp-server", "tcp", "google.com") if err != nil { + testenv.SkipFlakyNet(t) t.Errorf("LookupSRV(xmpp-server, tcp, google.com): %v (mode=%v)", err, mode) } else { if !strings.HasSuffix(cname, ".google.com.") { @@ -589,54 +609,40 @@ func srvString(srvs []*SRV) string { return buf.String() } -var lookupPortTests = []struct { - network string - name string - port int - ok bool -}{ - {"tcp", "0", 0, true}, - {"tcp", "echo", 7, true}, - {"tcp", "discard", 9, true}, - {"tcp", "systat", 11, true}, - {"tcp", "daytime", 13, true}, - {"tcp", "chargen", 19, true}, - {"tcp", "ftp-data", 20, true}, - {"tcp", "ftp", 21, true}, - {"tcp", "telnet", 23, true}, - {"tcp", "smtp", 25, true}, - {"tcp", "time", 37, true}, - {"tcp", "domain", 53, true}, - {"tcp", "finger", 79, true}, - {"tcp", "42", 42, true}, - - {"udp", "0", 0, true}, - {"udp", "echo", 7, true}, - {"udp", "tftp", 69, true}, - {"udp", "bootpc", 68, true}, - {"udp", "bootps", 67, true}, - {"udp", "domain", 53, true}, - {"udp", "ntp", 123, true}, - {"udp", "snmp", 161, true}, - {"udp", "syslog", 514, true}, - {"udp", "42", 42, true}, - - {"--badnet--", "zzz", 0, false}, - {"tcp", "--badport--", 0, false}, - {"tcp", "-1", 0, false}, - {"tcp", "65536", 0, false}, - {"udp", "-1", 0, false}, - {"udp", "65536", 0, false}, - {"tcp", "123456789", 0, false}, - - // Issue 13610: LookupPort("tcp", "") - {"tcp", "", 0, true}, - {"tcp6", "", 0, true}, - {"tcp4", "", 0, true}, - {"udp", "", 0, true}, -} - func TestLookupPort(t *testing.T) { + // See http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml + // + // Please be careful about adding new mappings for testings. + // There are platforms having incomplete mappings for + // restricted resource access and security reasons. + type test struct { + network string + name string + port int + ok bool + } + var tests = []test{ + {"tcp", "0", 0, true}, + {"udp", "0", 0, true}, + {"udp", "domain", 53, true}, + + {"--badnet--", "zzz", 0, false}, + {"tcp", "--badport--", 0, false}, + {"tcp", "-1", 0, false}, + {"tcp", "65536", 0, false}, + {"udp", "-1", 0, false}, + {"udp", "65536", 0, false}, + {"tcp", "123456789", 0, false}, + + // Issue 13610: LookupPort("tcp", "") + {"tcp", "", 0, true}, + {"tcp4", "", 0, true}, + {"tcp6", "", 0, true}, + {"udp", "", 0, true}, + {"udp4", "", 0, true}, + {"udp6", "", 0, true}, + } + switch runtime.GOOS { case "nacl": t.Skipf("not supported on %s", runtime.GOOS) @@ -644,11 +650,19 @@ func TestLookupPort(t *testing.T) { if netGo { t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS) } + default: + tests = append(tests, test{"tcp", "http", 80, true}) } - for _, tt := range lookupPortTests { - if port, err := LookupPort(tt.network, tt.name); port != tt.port || (err == nil) != tt.ok { + for _, tt := range tests { + port, err := LookupPort(tt.network, tt.name) + if port != tt.port || (err == nil) != tt.ok { t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok) } + if err != nil { + if perr := parseLookupPortError(err); perr != nil { + t.Error(perr) + } + } } } diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index 5461fe8a411e8d..15397e8105a92e 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -55,7 +55,7 @@ func lookupProtocol(_ context.Context, name string) (int, error) { func lookupHost(ctx context.Context, host string) (addrs []string, err error) { order := systemConf().hostLookupOrder(host) if order == hostLookupCgo { - if addrs, err, ok := cgoLookupHost(host); ok { + if addrs, err, ok := cgoLookupHost(ctx, host); ok { return addrs, err } // cgo not available (or netgo); fall back to Go's DNS resolver @@ -67,8 +67,7 @@ func lookupHost(ctx context.Context, host string) (addrs []string, err error) { func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { order := systemConf().hostLookupOrder(host) if order == hostLookupCgo { - // TODO(bradfitz): push down ctx, or at least its deadline to start - if addrs, err, ok := cgoLookupIP(host); ok { + if addrs, err, ok := cgoLookupIP(ctx, host); ok { return addrs, err } // cgo not available (or netgo); fall back to Go's DNS resolver @@ -84,7 +83,7 @@ func lookupPort(ctx context.Context, network, service string) (int, error) { // files might be on a remote filesystem, though. This should // probably race goroutines if ctx != context.Background(). if systemConf().canUseCgo() { - if port, err, ok := cgoLookupPort(network, service); ok { + if port, err, ok := cgoLookupPort(ctx, network, service); ok { return port, err } } @@ -93,8 +92,7 @@ func lookupPort(ctx context.Context, network, service string) (int, error) { func lookupCNAME(ctx context.Context, name string) (string, error) { if systemConf().canUseCgo() { - // TODO: use ctx. issue 15321. Or race goroutines. - if cname, err, ok := cgoLookupCNAME(name); ok { + if cname, err, ok := cgoLookupCNAME(ctx, name); ok { return cname, err } } @@ -161,7 +159,7 @@ func lookupTXT(ctx context.Context, name string) ([]string, error) { func lookupAddr(ctx context.Context, addr string) ([]string, error) { if systemConf().canUseCgo() { - if ptrs, err, ok := cgoLookupPTR(addr); ok { + if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok { return ptrs, err } } diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go index 9e6907c09a24cb..766de6a815b9df 100644 --- a/src/net/mockserver_test.go +++ b/src/net/mockserver_test.go @@ -184,28 +184,24 @@ func (dss *dualStackServer) teardown() error { return nil } -func newDualStackServer(lns []streamListener) (*dualStackServer, error) { - dss := &dualStackServer{lns: lns, port: "0"} - for i := range dss.lns { - ln, err := Listen(dss.lns[i].network, JoinHostPort(dss.lns[i].address, dss.port)) - if err != nil { - for _, ln := range dss.lns[:i] { - ln.Listener.Close() - } - return nil, err - } - dss.lns[i].Listener = ln - dss.lns[i].done = make(chan bool) - if dss.port == "0" { - if _, dss.port, err = SplitHostPort(ln.Addr().String()); err != nil { - for _, ln := range dss.lns { - ln.Listener.Close() - } - return nil, err - } - } +func newDualStackServer() (*dualStackServer, error) { + lns, err := newDualStackListener() + if err != nil { + return nil, err + } + _, port, err := SplitHostPort(lns[0].Addr().String()) + if err != nil { + lns[0].Close() + lns[1].Close() + return nil, err } - return dss, nil + return &dualStackServer{ + lns: []streamListener{ + {network: "tcp4", address: lns[0].Addr().String(), Listener: lns[0], done: make(chan bool)}, + {network: "tcp6", address: lns[1].Addr().String(), Listener: lns[1], done: make(chan bool)}, + }, + port: port, + }, nil } func transponder(ln Listener, ch chan<- error) { @@ -228,7 +224,7 @@ func transponder(ln Listener, ch chan<- error) { defer c.Close() network := ln.Addr().Network() - if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network { + if c.LocalAddr().Network() != network || c.RemoteAddr().Network() != network { ch <- fmt.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network) return } diff --git a/src/net/net.go b/src/net/net.go index 27e9ca367d4054..d6812d1ef054dc 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -14,7 +14,7 @@ the same interfaces and similar Dial and Listen functions. The Dial function connects to a server: - conn, err := net.Dial("tcp", "google.com:80") + conn, err := net.Dial("tcp", "golang.org:80") if err != nil { // handle error } diff --git a/src/net/net_test.go b/src/net/net_test.go index 94392928c2df60..b2f825daffc93c 100644 --- a/src/net/net_test.go +++ b/src/net/net_test.go @@ -360,3 +360,57 @@ func TestAcceptIgnoreAbortedConnRequest(t *testing.T) { t.Error(err) } } + +func TestZeroByteRead(t *testing.T) { + for _, network := range []string{"tcp", "unix", "unixpacket"} { + if !testableNetwork(network) { + t.Logf("skipping %s test", network) + continue + } + + ln, err := newLocalListener(network) + if err != nil { + t.Fatal(err) + } + connc := make(chan Conn, 1) + go func() { + defer ln.Close() + c, err := ln.Accept() + if err != nil { + t.Error(err) + } + connc <- c // might be nil + }() + c, err := Dial(network, ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + sc := <-connc + if sc == nil { + continue + } + defer sc.Close() + + if runtime.GOOS == "windows" { + // A zero byte read on Windows caused a wait for readability first. + // Rather than change that behavior, satisfy it in this test. + // See Issue 15735. + go io.WriteString(sc, "a") + } + + n, err := c.Read(nil) + if n != 0 || err != nil { + t.Errorf("%s: zero byte client read = %v, %v; want 0, nil", network, n, err) + } + + if runtime.GOOS == "windows" { + // Same as comment above. + go io.WriteString(c, "a") + } + n, err = sc.Read(nil) + if n != 0 || err != nil { + t.Errorf("%s: zero byte server read = %v, %v; want 0, nil", network, n, err) + } + } +} diff --git a/src/net/netgo_unix_test.go b/src/net/netgo_unix_test.go index 0a118874c25aef..5f1eb19e1240bd 100644 --- a/src/net/netgo_unix_test.go +++ b/src/net/netgo_unix_test.go @@ -14,14 +14,15 @@ import ( func TestGoLookupIP(t *testing.T) { host := "localhost" - _, err, ok := cgoLookupIP(host) + ctx := context.Background() + _, err, ok := cgoLookupIP(ctx, host) if ok { t.Errorf("cgoLookupIP must be a placeholder") } if err != nil { t.Error(err) } - if _, err := goLookupIP(context.Background(), host); err != nil { + if _, err := goLookupIP(ctx, host); err != nil { t.Error(err) } } diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index 86010927b321ef..7991a579fd1b34 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -124,7 +124,7 @@ func TestDialTimeoutMaxDuration(t *testing.T) { for i, tt := range dialTimeoutMaxDurationTests { ch := make(chan error) - max := time.NewTimer(100 * time.Millisecond) + max := time.NewTimer(250 * time.Millisecond) defer max.Stop() go func() { d := Dialer{Timeout: tt.timeout} diff --git a/src/net/url/url.go b/src/net/url/url.go index 05b41fa964a9cc..30e92779370393 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -3,9 +3,13 @@ // license that can be found in the LICENSE file. // Package url parses URLs and implements query escaping. -// See RFC 3986. package url +// See RFC 3986. This package generally follows RFC 3986, except where +// it deviates for compatibility reasons. When sending changes, first +// search old issues for history on decisions. Unit tests should also +// contain references to issue numbers with details. + import ( "bytes" "errors" @@ -573,12 +577,8 @@ func parseHost(host string) (string, error) { } return host1 + host2 + host3, nil } - } else if i := strings.LastIndex(host, ":"); i > 0 { - colonPort := host[i:] - if !validOptionalPort(colonPort) { - return "", fmt.Errorf("invalid port %q after host", colonPort) - } } + var err error if host, err = unescape(host, encodeHost); err != nil { return "", err diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index da6bc2843e52b0..7560f22c4a1e2a 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -418,10 +418,10 @@ var urltests = []URLTest{ }, // worst case host, still round trips { - "scheme://!$&'()*+,;=hello!:8080/path", + "scheme://!$&'()*+,;=hello!:port/path", &URL{ Scheme: "scheme", - Host: "!$&'()*+,;=hello!:8080", + Host: "!$&'()*+,;=hello!:port", Path: "/path", }, "", @@ -636,10 +636,8 @@ var parseRequestURLTests = []struct { {"*", true}, {"http://192.168.0.1/", true}, {"http://192.168.0.1:8080/", true}, - {"http://192.168.0.1:foo/", false}, {"http://[fe80::1]/", true}, {"http://[fe80::1]:8080/", true}, - {"http://[fe80::1]:foo/", false}, // Tests exercising RFC 6874 compliance: {"http://[fe80::1%25en0]/", true}, // with alphanum zone identifier diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index 5121b9b2ccca48..10300ce234a751 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -103,8 +103,9 @@ type Cmd struct { // available after a call to Wait or Run. ProcessState *os.ProcessState - lookPathErr error // LookPath error, if any. - finished bool // when Wait was called + ctx context.Context // nil means none + lookPathErr error // LookPath error, if any. + finished bool // when Wait was called childFiles []*os.File closeAfterStart []io.Closer closeAfterWait []io.Closer @@ -139,6 +140,20 @@ func Command(name string, arg ...string) *Cmd { return cmd } +// CommandContext is like Command but includes a context. +// +// The provided context is used to kill the process (by calling +// os.Process.Kill) if the context becomes done before the command +// completes on its own. +func CommandContext(ctx context.Context, name string, arg ...string) *Cmd { + if ctx == nil { + panic("nil Context") + } + cmd := Command(name, arg...) + cmd.ctx = ctx + return cmd +} + // interfaceEqual protects against panics from doing equality tests on // two interfaces with non-comparable underlying types. func interfaceEqual(a, b interface{}) bool { @@ -263,15 +278,6 @@ func (c *Cmd) Run() error { return c.Wait() } -// RunContext is like Run, but kills the process (by calling os.Process.Kill) -// if ctx is done before the process ends on its own. -func (c *Cmd) RunContext(ctx context.Context) error { - if err := c.Start(); err != nil { - return err - } - return c.WaitContext(ctx) -} - // lookExtensions finds windows executable by its dir and path. // It uses LookPath to try appropriate extensions. // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. @@ -396,12 +402,6 @@ func (e *ExitError) Error() string { // // Wait releases any resources associated with the Cmd. func (c *Cmd) Wait() error { - return c.WaitContext(nil) -} - -// WaitContext is like Wait, but kills the process (by calling os.Process.Kill) -// if ctx is done before the process ends on its own. -func (c *Cmd) WaitContext(ctx context.Context) error { if c.Process == nil { return errors.New("exec: not started") } @@ -411,11 +411,11 @@ func (c *Cmd) WaitContext(ctx context.Context) error { c.finished = true var waitDone chan struct{} - if ctx != nil { + if c.ctx != nil { waitDone = make(chan struct{}) go func() { select { - case <-ctx.Done(): + case <-c.ctx.Done(): c.Process.Kill() case <-waitDone: } diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index 1151ca7d0f0c26..41f9dfe1c63614 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -29,16 +29,24 @@ import ( "time" ) -func helperCommand(t *testing.T, s ...string) *exec.Cmd { +func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) { testenv.MustHaveExec(t) cs := []string{"-test.run=TestHelperProcess", "--"} cs = append(cs, s...) - cmd := exec.Command(os.Args[0], cs...) + if ctx != nil { + cmd = exec.CommandContext(ctx, os.Args[0], cs...) + } else { + cmd = exec.Command(os.Args[0], cs...) + } cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} return cmd } +func helperCommand(t *testing.T, s ...string) *exec.Cmd { + return helperCommandContext(t, nil, s...) +} + func TestEcho(t *testing.T) { bs, err := helperCommand(t, "echo", "foo bar", "baz").Output() if err != nil { @@ -660,10 +668,6 @@ func TestHelperProcess(*testing.T) { // the cloned file descriptors that result from opening // /dev/urandom. // https://golang.org/issue/3955 - case "plan9": - // TODO(0intro): Determine why Plan 9 is leaking - // file descriptors. - // https://golang.org/issue/7118 case "solaris": // TODO(aram): This fails on Solaris because libc opens // its own files, as it sees fit. Darwin does the same, @@ -838,7 +842,8 @@ func TestOutputStderrCapture(t *testing.T) { } func TestContext(t *testing.T) { - c := helperCommand(t, "pipetest") + ctx, cancel := context.WithCancel(context.Background()) + c := helperCommandContext(t, ctx, "pipetest") stdin, err := c.StdinPipe() if err != nil { t.Fatal(err) @@ -847,7 +852,6 @@ func TestContext(t *testing.T) { if err != nil { t.Fatal(err) } - ctx, cancel := context.WithCancel(context.Background()) if err := c.Start(); err != nil { t.Fatal(err) } @@ -862,7 +866,7 @@ func TestContext(t *testing.T) { } waitErr := make(chan error, 1) go func() { - waitErr <- c.WaitContext(ctx) + waitErr <- c.Wait() }() cancel() select { diff --git a/src/os/exec/lp_plan9.go b/src/os/exec/lp_plan9.go index 82678802a96483..142f87ed32b3b5 100644 --- a/src/os/exec/lp_plan9.go +++ b/src/os/exec/lp_plan9.go @@ -7,6 +7,7 @@ package exec import ( "errors" "os" + "path/filepath" "strings" ) @@ -44,9 +45,10 @@ func LookPath(file string) (string, error) { } path := os.Getenv("path") - for _, dir := range strings.Split(path, "\000") { - if err := findExecutable(dir + "/" + file); err == nil { - return dir + "/" + file, nil + for _, dir := range filepath.SplitList(path) { + path := filepath.Join(dir, file) + if err := findExecutable(path); err == nil { + return path, nil } } return "", &Error{file, ErrNotFound} diff --git a/src/os/exec/lp_unix.go b/src/os/exec/lp_unix.go index 32e3046cb87401..7a302752a8974d 100644 --- a/src/os/exec/lp_unix.go +++ b/src/os/exec/lp_unix.go @@ -9,6 +9,7 @@ package exec import ( "errors" "os" + "path/filepath" "strings" ) @@ -42,16 +43,13 @@ func LookPath(file string) (string, error) { } return "", &Error{file, err} } - pathenv := os.Getenv("PATH") - if pathenv == "" { - return "", &Error{file, ErrNotFound} - } - for _, dir := range strings.Split(pathenv, ":") { + path := os.Getenv("PATH") + for _, dir := range filepath.SplitList(path) { if dir == "" { // Unix shell semantics: path element "" means "." dir = "." } - path := dir + "/" + file + path := filepath.Join(dir, file) if err := findExecutable(path); err == nil { return path, nil } diff --git a/src/os/exec/lp_windows.go b/src/os/exec/lp_windows.go index 1c005220d01bc3..793d4d98b3a6ae 100644 --- a/src/os/exec/lp_windows.go +++ b/src/os/exec/lp_windows.go @@ -7,6 +7,7 @@ package exec import ( "errors" "os" + "path/filepath" "strings" ) @@ -56,20 +57,22 @@ func findExecutable(file string, exts []string) (string, error) { // a suitable candidate. // The result may be an absolute path or a path relative to the current directory. func LookPath(file string) (string, error) { + var exts []string x := os.Getenv(`PATHEXT`) - if x == "" { - x = `.COM;.EXE;.BAT;.CMD` - } - exts := []string{} - for _, e := range strings.Split(strings.ToLower(x), `;`) { - if e == "" { - continue - } - if e[0] != '.' { - e = "." + e + if x != "" { + for _, e := range strings.Split(strings.ToLower(x), `;`) { + if e == "" { + continue + } + if e[0] != '.' { + e = "." + e + } + exts = append(exts, e) } - exts = append(exts, e) + } else { + exts = []string{".com", ".exe", ".bat", ".cmd"} } + if strings.ContainsAny(file, `:\/`) { if f, err := findExecutable(file, exts); err == nil { return f, nil @@ -77,48 +80,14 @@ func LookPath(file string) (string, error) { return "", &Error{file, err} } } - if f, err := findExecutable(`.\`+file, exts); err == nil { + if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { return f, nil } - if pathenv := os.Getenv(`PATH`); pathenv != "" { - for _, dir := range splitList(pathenv) { - if f, err := findExecutable(dir+`\`+file, exts); err == nil { - return f, nil - } + path := os.Getenv("path") + for _, dir := range filepath.SplitList(path) { + if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { + return f, nil } } return "", &Error{file, ErrNotFound} } - -func splitList(path string) []string { - // The same implementation is used in SplitList in path/filepath; - // consider changing path/filepath when changing this. - - if path == "" { - return []string{} - } - - // Split path, respecting but preserving quotes. - list := []string{} - start := 0 - quo := false - for i := 0; i < len(path); i++ { - switch c := path[i]; { - case c == '"': - quo = !quo - case c == os.PathListSeparator && !quo: - list = append(list, path[start:i]) - start = i + 1 - } - } - list = append(list, path[start:]) - - // Remove quotes. - for i, s := range list { - if strings.Contains(s, `"`) { - list[i] = strings.Replace(s, `"`, "", -1) - } - } - - return list -} diff --git a/src/os/exec/lp_windows_test.go b/src/os/exec/lp_windows_test.go index 042e5a1389eb7d..96a22d843f8544 100644 --- a/src/os/exec/lp_windows_test.go +++ b/src/os/exec/lp_windows_test.go @@ -107,7 +107,7 @@ func createEnv(dir, PATH, PATHEXT string) []string { env := os.Environ() env = updateEnv(env, "PATHEXT", PATHEXT) // Add dir in front of every directory in the PATH. - dirs := splitList(PATH) + dirs := filepath.SplitList(PATH) for i := range dirs { dirs[i] = filepath.Join(dir, dirs[i]) } diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go index 0fe1b8213dc54b..9edb6bc0747d08 100644 --- a/src/os/file_plan9.go +++ b/src/os/file_plan9.go @@ -5,6 +5,7 @@ package os import ( + "io" "runtime" "syscall" "time" @@ -123,7 +124,7 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) { } if append { - if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil { + if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil { return nil, &PathError{"seek", name, e} } } @@ -145,11 +146,9 @@ func (file *file) close() error { return ErrInvalid } var err error - syscall.ForkLock.RLock() if e := syscall.Close(file.fd); e != nil { err = &PathError{"close", file.name, e} } - syscall.ForkLock.RUnlock() file.fd = -1 // so it can't be closed again // no need for a finalizer anymore @@ -419,12 +418,9 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error { func Pipe() (r *File, w *File, err error) { var p [2]int - syscall.ForkLock.RLock() if e := syscall.Pipe(p[0:]); e != nil { - syscall.ForkLock.RUnlock() return nil, nil, NewSyscallError("pipe", e) } - syscall.ForkLock.RUnlock() return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil } diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 137f24a0a95979..f470fc4315cc2b 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -325,11 +325,11 @@ func (f *File) read(b []byte) (n int, err error) { func (f *File) pread(b []byte, off int64) (n int, err error) { f.l.Lock() defer f.l.Unlock() - curoffset, e := syscall.Seek(f.fd, 0, 1) + curoffset, e := syscall.Seek(f.fd, 0, io.SeekCurrent) if e != nil { return 0, e } - defer syscall.Seek(f.fd, curoffset, 0) + defer syscall.Seek(f.fd, curoffset, io.SeekStart) o := syscall.Overlapped{ OffsetHigh: uint32(off >> 32), Offset: uint32(off), @@ -405,11 +405,11 @@ func (f *File) write(b []byte) (n int, err error) { func (f *File) pwrite(b []byte, off int64) (n int, err error) { f.l.Lock() defer f.l.Unlock() - curoffset, e := syscall.Seek(f.fd, 0, 1) + curoffset, e := syscall.Seek(f.fd, 0, io.SeekCurrent) if e != nil { return 0, e } - defer syscall.Seek(f.fd, curoffset, 0) + defer syscall.Seek(f.fd, curoffset, io.SeekStart) o := syscall.Overlapped{ OffsetHigh: uint32(off >> 32), Offset: uint32(off), @@ -474,6 +474,12 @@ func Remove(name string) error { } else { if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { e = e1 + } else if a&syscall.FILE_ATTRIBUTE_READONLY != 0 { + if e1 = syscall.SetFileAttributes(p, a&^syscall.FILE_ATTRIBUTE_READONLY); e1 == nil { + if e = syscall.DeleteFile(p); e == nil { + return nil + } + } } } } diff --git a/src/os/os_test.go b/src/os/os_test.go index 8f62902a6caaf8..baa2f07fd2b0f3 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -1182,14 +1182,14 @@ func TestSeek(t *testing.T) { out int64 } var tests = []test{ - {0, 1, int64(len(data))}, - {0, 0, 0}, - {5, 0, 5}, - {0, 2, int64(len(data))}, - {0, 0, 0}, - {-1, 2, int64(len(data)) - 1}, - {1 << 33, 0, 1 << 33}, - {1 << 33, 2, 1<<33 + int64(len(data))}, + {0, io.SeekCurrent, int64(len(data))}, + {0, io.SeekStart, 0}, + {5, io.SeekStart, 5}, + {0, io.SeekEnd, int64(len(data))}, + {0, io.SeekStart, 0}, + {-1, io.SeekEnd, int64(len(data)) - 1}, + {1 << 33, io.SeekStart, 1 << 33}, + {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))}, } for i, tt := range tests { off, err := f.Seek(tt.in, tt.whence) @@ -1376,6 +1376,38 @@ func TestReadAt(t *testing.T) { } } +// Verify that ReadAt doesn't affect seek offset. +// In the Plan 9 kernel, there used to be a bug in the implementation of +// the pread syscall, where the channel offset was erroneously updated after +// calling pread on a file. +func TestReadAtOffset(t *testing.T) { + f := newFile("TestReadAtOffset", t) + defer Remove(f.Name()) + defer f.Close() + + const data = "hello, world\n" + io.WriteString(f, data) + + f.Seek(0, 0) + b := make([]byte, 5) + + n, err := f.ReadAt(b, 7) + if err != nil || n != len(b) { + t.Fatalf("ReadAt 7: %d, %v", n, err) + } + if string(b) != "world" { + t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") + } + + n, err = f.Read(b) + if err != nil || n != len(b) { + t.Fatalf("Read: %d, %v", n, err) + } + if string(b) != "hello" { + t.Fatalf("Read: have %q want %q", string(b), "hello") + } +} + func TestWriteAt(t *testing.T) { f := newFile("TestWriteAt", t) defer Remove(f.Name()) @@ -1726,7 +1758,7 @@ var nilFileMethodTests = []struct { {"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }}, {"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }}, {"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }}, - {"Seek", func(f *File) error { _, err := f.Seek(0, 0); return err }}, + {"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }}, {"Stat", func(f *File) error { _, err := f.Stat(); return err }}, {"Sync", func(f *File) error { return f.Sync() }}, {"Truncate", func(f *File) error { return f.Truncate(0) }}, diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go index c47f5462ab56c8..5c10154760cbe7 100644 --- a/src/os/os_unix_test.go +++ b/src/os/os_unix_test.go @@ -145,6 +145,9 @@ func TestLchown(t *testing.T) { linkname := f.Name() + "2" if err := Symlink(f.Name(), linkname); err != nil { + if runtime.GOOS == "android" && IsPermission(err) { + t.Skip("skipping test on Android; permission error creating symlink") + } t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err) } defer Remove(linkname) diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 2f7d48d5bdf06c..05d7a8f34e9a9f 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -223,3 +223,25 @@ func TestOpenVolumeName(t *testing.T) { t.Fatalf("unexpected file list %q, want %q", have, want) } } + +func TestDeleteReadOnly(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "TestDeleteReadOnly") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + p := filepath.Join(tmpdir, "a") + // This sets FILE_ATTRIBUTE_READONLY. + f, err := os.OpenFile(p, os.O_CREATE, 0400) + if err != nil { + t.Fatal(err) + } + f.Close() + + if err = os.Chmod(p, 0400); err != nil { + t.Fatal(err) + } + if err = os.Remove(p); err != nil { + t.Fatal(err) + } +} diff --git a/src/os/signal/doc.go b/src/os/signal/doc.go index 9ee547b15db420..73b01a2764de60 100644 --- a/src/os/signal/doc.go +++ b/src/os/signal/doc.go @@ -205,8 +205,8 @@ before raising the signal. Windows On Windows a ^C (Control-C) or ^BREAK (Control-Break) normally cause -the program to exit. If Notify is called for os.SIGINT, ^C or ^BREAK -will cause os.SIGINT to be sent on the channel, and the program will +the program to exit. If Notify is called for os.Interrupt, ^C or ^BREAK +will cause os.Interrupt to be sent on the channel, and the program will not exit. If Reset is called, or Stop is called on all channels passed to Notify, then the default behavior will be restored. diff --git a/src/os/user/getgrouplist_darwin.c b/src/os/user/getgrouplist_darwin.go similarity index 64% rename from src/os/user/getgrouplist_darwin.c rename to src/os/user/getgrouplist_darwin.go index 6ad561489829ea..54a2da3610434c 100644 --- a/src/os/user/getgrouplist_darwin.c +++ b/src/os/user/getgrouplist_darwin.go @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build cgo +package user +/* #include #include #include -int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { +static int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { int* buf = malloc(*ngroups * sizeof(int)); int rv = getgrouplist(user, (int) group, buf, ngroups); int i; @@ -20,3 +21,9 @@ int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { free(buf); return rv; } +*/ +import "C" + +func getGroupList(name *C.char, userGID C.gid_t, gids *C.gid_t, n *C.int) C.int { + return C.mygetgrouplist(name, userGID, gids, n) +} diff --git a/src/os/user/getgrouplist_unix.c b/src/os/user/getgrouplist_unix.go similarity index 56% rename from src/os/user/getgrouplist_unix.c rename to src/os/user/getgrouplist_unix.go index eb14f9ab8a0ac1..14da7c00a2b55d 100644 --- a/src/os/user/getgrouplist_unix.c +++ b/src/os/user/getgrouplist_unix.go @@ -2,13 +2,21 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build cgo // +build dragonfly freebsd !android,linux netbsd openbsd +package user + +/* #include #include #include -int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { +static int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups) { return getgrouplist(user, group, groups, ngroups); } +*/ +import "C" + +func getGroupList(name *C.char, userGID C.gid_t, gids *C.gid_t, n *C.int) C.int { + return C.mygetgrouplist(name, userGID, gids, n) +} diff --git a/src/os/user/listgroups_unix.go b/src/os/user/listgroups_unix.go index f78baaac1e27f7..db952c64bffdb3 100644 --- a/src/os/user/listgroups_unix.go +++ b/src/os/user/listgroups_unix.go @@ -16,8 +16,6 @@ import ( #include #include #include - -extern int mygetgrouplist(const char* user, gid_t group, gid_t* groups, int* ngroups); */ import "C" @@ -32,7 +30,7 @@ func listGroups(u *User) ([]string, error) { n := C.int(256) gidsC := make([]C.gid_t, n) - rv := C.mygetgrouplist(nameC, userGID, &gidsC[0], &n) + rv := getGroupList(nameC, userGID, &gidsC[0], &n) if rv == -1 { // More than initial buffer, but now n contains the correct size. const maxGroups = 2048 @@ -40,7 +38,7 @@ func listGroups(u *User) ([]string, error) { return nil, fmt.Errorf("user: list groups for %s: member of more than %d groups", u.Username, maxGroups) } gidsC = make([]C.gid_t, n) - rv := C.mygetgrouplist(nameC, userGID, &gidsC[0], &n) + rv := getGroupList(nameC, userGID, &gidsC[0], &n) if rv == -1 { return nil, fmt.Errorf("user: list groups for %s failed (changed groups?)", u.Username) } diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go index d64bf84fc0a9ef..2adb0c7490821e 100644 --- a/src/path/filepath/match.go +++ b/src/path/filepath/match.go @@ -240,13 +240,10 @@ func Glob(pattern string) (matches []string, err error) { } dir, file := Split(pattern) - switch dir { - case "": - dir = "." - case string(Separator): - // nothing - default: - dir = dir[0 : len(dir)-1] // chop off trailing separator + if runtime.GOOS == "windows" { + dir = cleanGlobPathWindows(dir) + } else { + dir = cleanGlobPath(dir) } if !hasMeta(dir) { @@ -267,6 +264,35 @@ func Glob(pattern string) (matches []string, err error) { return } +// cleanGlobPath prepares path for glob matching. +func cleanGlobPath(path string) string { + switch path { + case "": + return "." + case string(Separator): + // do nothing to the path + return path + default: + return path[0 : len(path)-1] // chop off trailing separator + } +} + +// cleanGlobPathWindows is windows version of cleanGlobPath. +func cleanGlobPathWindows(path string) string { + vollen := volumeNameLen(path) + switch { + case path == "": + return "." + case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/ + // do nothing to the path + return path + case vollen == len(path) && len(path) == 2: // C: + return path + "." // convert C: into C:. + default: + return path[0 : len(path)-1] // chop off trailing separator + } +} + // glob searches for files matching pattern in the directory dir // and appends them to matches. If the directory cannot be // opened, it returns the existing matches. New matches are diff --git a/src/path/filepath/match_test.go b/src/path/filepath/match_test.go index d8bab7f4da3969..8dcfa5972e551f 100644 --- a/src/path/filepath/match_test.go +++ b/src/path/filepath/match_test.go @@ -5,10 +5,12 @@ package filepath_test import ( + "fmt" "io/ioutil" "os" . "path/filepath" "runtime" + "sort" "strings" "testing" ) @@ -209,3 +211,164 @@ func TestGlobSymlink(t *testing.T) { } } } + +type globTest struct { + pattern string + matches []string +} + +func (test *globTest) buildWant(root string) []string { + want := make([]string, 0) + for _, m := range test.matches { + want = append(want, root+FromSlash(m)) + } + sort.Strings(want) + return want +} + +func (test *globTest) globAbs(root, rootPattern string) error { + p := FromSlash(rootPattern + `\` + test.pattern) + have, err := Glob(p) + if err != nil { + return err + } + sort.Strings(have) + want := test.buildWant(root + `\`) + if strings.Join(want, "_") == strings.Join(have, "_") { + return nil + } + return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) +} + +func (test *globTest) globRel(root string) error { + p := root + FromSlash(test.pattern) + have, err := Glob(p) + if err != nil { + return err + } + sort.Strings(have) + want := test.buildWant(root) + if strings.Join(want, "_") == strings.Join(have, "_") { + return nil + } + // try also matching version without root prefix + wantWithNoRoot := test.buildWant("") + if strings.Join(wantWithNoRoot, "_") == strings.Join(have, "_") { + return nil + } + return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) +} + +func TestWindowsGlob(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skipf("skipping windows specific test") + } + + tmpDir, err := ioutil.TempDir("", "TestWindowsGlob") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + // /tmp may itself be a symlink + tmpDir, err = EvalSymlinks(tmpDir) + if err != nil { + t.Fatal("eval symlink for tmp dir:", err) + } + + if len(tmpDir) < 3 { + t.Fatalf("tmpDir path %q is too short", tmpDir) + } + if tmpDir[1] != ':' { + t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir) + } + + dirs := []string{ + "a", + "b", + "dir/d/bin", + } + files := []string{ + "dir/d/bin/git.exe", + } + for _, dir := range dirs { + err := os.MkdirAll(Join(tmpDir, dir), 0777) + if err != nil { + t.Fatal(err) + } + } + for _, file := range files { + err := ioutil.WriteFile(Join(tmpDir, file), nil, 0666) + if err != nil { + t.Fatal(err) + } + } + + tests := []globTest{ + {"a", []string{"a"}}, + {"b", []string{"b"}}, + {"c", []string{}}, + {"*", []string{"a", "b", "dir"}}, + {"d*", []string{"dir"}}, + {"*i*", []string{"dir"}}, + {"*r", []string{"dir"}}, + {"?ir", []string{"dir"}}, + {"?r", []string{}}, + {"d*/*/bin/git.exe", []string{"dir/d/bin/git.exe"}}, + } + + // test absolute paths + for _, test := range tests { + var p string + err = test.globAbs(tmpDir, tmpDir) + if err != nil { + t.Error(err) + } + // test C:\*Documents and Settings\... + p = tmpDir + p = strings.Replace(p, `:\`, `:\*`, 1) + err = test.globAbs(tmpDir, p) + if err != nil { + t.Error(err) + } + // test C:\Documents and Settings*\... + p = tmpDir + p = strings.Replace(p, `:\`, `:`, 1) + p = strings.Replace(p, `\`, `*\`, 1) + p = strings.Replace(p, `:`, `:\`, 1) + err = test.globAbs(tmpDir, p) + if err != nil { + t.Error(err) + } + } + + // test relative paths + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + err = os.Chdir(tmpDir) + if err != nil { + t.Fatal(err) + } + defer func() { + err := os.Chdir(wd) + if err != nil { + t.Fatal(err) + } + }() + for _, test := range tests { + err := test.globRel("") + if err != nil { + t.Error(err) + } + err = test.globRel(`.\`) + if err != nil { + t.Error(err) + } + err = test.globRel(tmpDir[:2]) // C: + if err != nil { + t.Error(err) + } + } +} diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index d4c3e4e588a89d..f7cf46daecf524 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -1889,32 +1889,6 @@ type Tbigp [2]uintptr func (p *Tbigp) M(x int, b byte) (byte, int) { return b, x + int(p[0]) + int(p[1]) } -// Again, with an unexported method. - -type tsmallv byte - -func (v tsmallv) m(x int, b byte) (byte, int) { return b, x + int(v) } - -type tsmallp byte - -func (p *tsmallp) m(x int, b byte) (byte, int) { return b, x + int(*p) } - -type twordv uintptr - -func (v twordv) m(x int, b byte) (byte, int) { return b, x + int(v) } - -type twordp uintptr - -func (p *twordp) m(x int, b byte) (byte, int) { return b, x + int(*p) } - -type tbigv [2]uintptr - -func (v tbigv) m(x int, b byte) (byte, int) { return b, x + int(v[0]) + int(v[1]) } - -type tbigp [2]uintptr - -func (p *tbigp) m(x int, b byte) (byte, int) { return b, x + int(p[0]) + int(p[1]) } - type tinter interface { m(int, byte) (byte, int) } @@ -1958,7 +1932,6 @@ func TestMethod5(t *testing.T) { } var TinterType = TypeOf(new(Tinter)).Elem() - var tinterType = TypeOf(new(tinter)).Elem() CheckI := func(name string, i interface{}, inc int) { v := ValueOf(i) @@ -2000,39 +1973,6 @@ func TestMethod5(t *testing.T) { CheckI("t1", t1, 40) CheckI("&t1", &t1, 40) - methodShouldPanic := func(name string, i interface{}) { - v := ValueOf(i) - m := v.Method(0) - shouldPanic(func() { m.Call([]Value{ValueOf(1000), ValueOf(byte(99))}) }) - shouldPanic(func() { m.Interface() }) - - v = v.Convert(tinterType) - m = v.Method(0) - shouldPanic(func() { m.Call([]Value{ValueOf(1000), ValueOf(byte(99))}) }) - shouldPanic(func() { m.Interface() }) - } - - _sv := tsmallv(1) - methodShouldPanic("_sv", _sv) - methodShouldPanic("&_sv", &_sv) - - _sp := tsmallp(2) - methodShouldPanic("&_sp", &_sp) - - _wv := twordv(3) - methodShouldPanic("_wv", _wv) - methodShouldPanic("&_wv", &_wv) - - _wp := twordp(4) - methodShouldPanic("&_wp", &_wp) - - _bv := tbigv([2]uintptr{5, 6}) - methodShouldPanic("_bv", _bv) - methodShouldPanic("&_bv", &_bv) - - _bp := tbigp([2]uintptr{7, 8}) - methodShouldPanic("&_bp", &_bp) - var tnil Tinter vnil := ValueOf(&tnil).Elem() shouldPanic(func() { vnil.Method(0) }) @@ -2388,13 +2328,13 @@ type outer struct { inner } -func (*inner) m() {} -func (*outer) m() {} +func (*inner) M() {} +func (*outer) M() {} func TestNestedMethods(t *testing.T) { typ := TypeOf((*outer)(nil)) - if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).m).Pointer() { - t.Errorf("Wrong method table for outer: (m=%p)", (*outer).m) + if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).M).Pointer() { + t.Errorf("Wrong method table for outer: (M=%p)", (*outer).M) for i := 0; i < typ.NumMethod(); i++ { m := typ.Method(i) t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer()) @@ -2416,17 +2356,8 @@ var unexpi unexpI = new(unexp) func TestUnexportedMethods(t *testing.T) { typ := TypeOf(unexpi) - if typ.Method(0).Type == nil { - t.Error("missing type for satisfied method 'f'") - } - if !typ.Method(0).Func.IsValid() { - t.Error("missing func for satisfied method 'f'") - } - if typ.Method(1).Type != nil { - t.Error("found type for unsatisfied method 'g'") - } - if typ.Method(1).Func.IsValid() { - t.Error("found func for unsatisfied method 'g'") + if got := typ.NumMethod(); got != 0 { + t.Errorf("NumMethod=%d, want 0 satisfied methods", got) } } @@ -2918,12 +2849,11 @@ func TestUnexported(t *testing.T) { isValid(v.Elem().Field(1)) isValid(v.Elem().FieldByName("x")) isValid(v.Elem().FieldByName("y")) - isValid(v.Type().Method(0).Func) shouldPanic(func() { v.Elem().Field(0).Interface() }) shouldPanic(func() { v.Elem().Field(1).Interface() }) shouldPanic(func() { v.Elem().FieldByName("x").Interface() }) shouldPanic(func() { v.Elem().FieldByName("y").Interface() }) - shouldPanic(func() { v.Type().Method(0).Func.Interface() }) + shouldPanic(func() { v.Type().Method(0) }) } func TestSetPanic(t *testing.T) { @@ -4210,7 +4140,7 @@ func TestStructOfExportRules(t *testing.T) { } exported := isExported(n) if exported != test.exported { - t.Errorf("test-%d: got exported=%v want exported=%v", exported, test.exported) + t.Errorf("test-%d: got exported=%v want exported=%v", i, exported, test.exported) } }) } @@ -4520,7 +4450,7 @@ func TestStructOfWithInterface(t *testing.T) { if table.impl { t.Errorf("test-%d: type=%v fails to implement Iface.\n", i, table.typ) } else { - t.Errorf("test-%d: type=%v should NOT implement Iface\n", table.typ) + t.Errorf("test-%d: type=%v should NOT implement Iface\n", i, table.typ) } continue } @@ -4748,7 +4678,7 @@ func TestFuncOf(t *testing.T) { if len(args) != 1 { t.Errorf("args == %v, want exactly one arg", args) } else if args[0].Type() != TypeOf(K("")) { - t.Errorf("args[0] is type %v, want %v", args[0].Type, TypeOf(K(""))) + t.Errorf("args[0] is type %v, want %v", args[0].Type(), TypeOf(K(""))) } else if args[0].String() != "gopher" { t.Errorf("args[0] = %q, want %q", args[0].String(), "gopher") } @@ -4760,7 +4690,7 @@ func TestFuncOf(t *testing.T) { if len(outs) != 1 { t.Fatalf("v.Call returned %v, want exactly one result", outs) } else if outs[0].Type() != TypeOf(V(0)) { - t.Fatalf("c.Call[0] is type %v, want %v", outs[0].Type, TypeOf(V(0))) + t.Fatalf("c.Call[0] is type %v, want %v", outs[0].Type(), TypeOf(V(0))) } f := outs[0].Float() if f != 3.14 { @@ -5187,7 +5117,7 @@ func useStack(n int) { type Impl struct{} -func (Impl) f() {} +func (Impl) F() {} func TestValueString(t *testing.T) { rv := ValueOf(Impl{}) @@ -5221,6 +5151,41 @@ func TestLargeGCProg(t *testing.T) { fv.Call([]Value{ValueOf([256]*byte{})}) } +func fieldIndexRecover(t Type, i int) (recovered interface{}) { + defer func() { + recovered = recover() + }() + + t.Field(i) + return +} + +// Issue 15046. +func TestTypeFieldOutOfRangePanic(t *testing.T) { + typ := TypeOf(struct{ X int }{10}) + testIndices := [...]struct { + i int + mustPanic bool + }{ + 0: {-2, true}, + 1: {0, false}, + 2: {1, true}, + 3: {1 << 10, true}, + } + for i, tt := range testIndices { + recoveredErr := fieldIndexRecover(typ, tt.i) + if tt.mustPanic { + if recoveredErr == nil { + t.Errorf("#%d: fieldIndex %d expected to panic", i, tt.i) + } + } else { + if recoveredErr != nil { + t.Errorf("#%d: got err=%v, expected no panic", i, recoveredErr) + } + } + } +} + // Issue 9179. func TestCallGC(t *testing.T) { f := func(a, b, c, d, e string) { @@ -5737,13 +5702,38 @@ func TestNameBytesAreAligned(t *testing.T) { } } -func TestMethodPkgPathReadable(t *testing.T) { - // Reading the Method type for an unexported method triggers an - // offset resolution via p.name.pkgPath(). Make sure it uses a - // valid base pointer for the offset. - v := ValueOf(embed{}) - m := v.Type().Method(0) - if m.PkgPath != "reflect" { - t.Errorf(`PkgPath=%q, want "reflect"`, m.PkgPath) +func TestTypeStrings(t *testing.T) { + type stringTest struct { + typ Type + want string + } + stringTests := []stringTest{ + {TypeOf(func(int) {}), "func(int)"}, + {FuncOf([]Type{TypeOf(int(0))}, nil, false), "func(int)"}, + {TypeOf(XM{}), "reflect_test.XM"}, + {TypeOf(new(XM)), "*reflect_test.XM"}, + {TypeOf(new(XM).String), "func() string"}, + {TypeOf(new(XM)).Method(0).Type, "func(*reflect_test.XM) string"}, + } + + for i, test := range stringTests { + if got, want := test.typ.String(), test.want; got != want { + t.Errorf("type %d String()=%q, want %q", i, got, want) + } + } +} + +func TestOffsetLock(t *testing.T) { + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + i := i + wg.Add(1) + go func() { + for j := 0; j < 50; j++ { + ResolveReflectName(fmt.Sprintf("OffsetLockName:%d:%d", i, j)) + } + wg.Done() + }() } + wg.Wait() } diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index 00189f3353f196..2cc1530250c084 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -109,3 +109,7 @@ func IsExported(t Type) bool { n := typ.nameOff(typ.str) return n.isExported() } + +func ResolveReflectName(s string) { + resolveReflectName(newName(s, "", "", false)) +} diff --git a/src/reflect/type.go b/src/reflect/type.go index 2ceb3d3f661726..1dff74df62a06f 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -763,16 +763,62 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 } func (t *rtype) common() *rtype { return t } +var methodCache struct { + sync.RWMutex + m map[*rtype][]method +} + +func (t *rtype) exportedMethods() []method { + methodCache.RLock() + methods, found := methodCache.m[t] + methodCache.RUnlock() + + if found { + return methods + } + + ut := t.uncommon() + if ut == nil { + return nil + } + allm := ut.methods() + allExported := true + for _, m := range allm { + name := t.nameOff(m.name) + if !name.isExported() { + allExported = false + break + } + } + if allExported { + methods = allm + } else { + methods = make([]method, 0, len(allm)) + for _, m := range allm { + name := t.nameOff(m.name) + if name.isExported() { + methods = append(methods, m) + } + } + methods = methods[:len(methods):len(methods)] + } + + methodCache.Lock() + if methodCache.m == nil { + methodCache.m = make(map[*rtype][]method) + } + methodCache.m[t] = methods + methodCache.Unlock() + + return methods +} + func (t *rtype) NumMethod() int { if t.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.NumMethod() } - ut := t.uncommon() - if ut == nil { - return 0 - } - return int(ut.mcount) + return len(t.exportedMethods()) } func (t *rtype) Method(i int) (m Method) { @@ -780,40 +826,31 @@ func (t *rtype) Method(i int) (m Method) { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.Method(i) } - ut := t.uncommon() - - if ut == nil || i < 0 || i >= int(ut.mcount) { + methods := t.exportedMethods() + if i < 0 || i >= len(methods) { panic("reflect: Method index out of range") } - p := ut.methods()[i] + p := methods[i] pname := t.nameOff(p.name) m.Name = pname.name() fl := flag(Func) - if !pname.isExported() { - m.PkgPath = pname.pkgPath() - if m.PkgPath == "" { - m.PkgPath = t.nameOff(ut.pkgPath).name() - } - fl |= flagStickyRO - } - if p.mtyp != 0 { - mtyp := t.typeOff(p.mtyp) - ft := (*funcType)(unsafe.Pointer(mtyp)) - in := make([]Type, 0, 1+len(ft.in())) - in = append(in, t) - for _, arg := range ft.in() { - in = append(in, arg) - } - out := make([]Type, 0, len(ft.out())) - for _, ret := range ft.out() { - out = append(out, ret) - } - mt := FuncOf(in, out, ft.IsVariadic()) - m.Type = mt - tfn := t.textOff(p.tfn) - fn := unsafe.Pointer(&tfn) - m.Func = Value{mt.(*rtype), fn, fl} - } + mtyp := t.typeOff(p.mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) + in := make([]Type, 0, 1+len(ft.in())) + in = append(in, t) + for _, arg := range ft.in() { + in = append(in, arg) + } + out := make([]Type, 0, len(ft.out())) + for _, ret := range ft.out() { + out = append(out, ret) + } + mt := FuncOf(in, out, ft.IsVariadic()) + m.Type = mt + tfn := t.textOff(p.tfn) + fn := unsafe.Pointer(&tfn) + m.Func = Value{mt.(*rtype), fn, fl} + m.Index = i return m } @@ -831,7 +868,7 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { for i := 0; i < int(ut.mcount); i++ { p := utmethods[i] pname := t.nameOff(p.name) - if pname.name() == name { + if pname.isExported() && pname.name() == name { return t.Method(i), true } } @@ -1178,7 +1215,7 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) { // Field returns the i'th struct field. func (t *structType) Field(i int) (f StructField) { if i < 0 || i >= len(t.fields) { - return + panic("reflect: Field index out of bounds") } p := &t.fields[i] f.Type = toType(p.typ) @@ -1985,6 +2022,7 @@ func FuncOf(in, out []Type, variadic bool) Type { if len(args) > 50 { panic("reflect.FuncOf does not support more than 50 arguments") } + ft.tflag = 0 ft.hash = hash ft.inCount = uint16(len(in)) ft.outCount = uint16(len(out)) @@ -2313,10 +2351,6 @@ type structTypeFixed32 struct { // StructOf returns the struct type containing fields. // The Offset and Index fields are ignored and computed as they would be // by the compiler. -// -// StructOf does not support creating structs with UTF-8 field names or -// UTF-8 (embedded) type names. -// This limitation may be lifted eventually. func StructOf(fields []StructField) Type { var ( hash = fnv1(0, []byte("struct {")...) @@ -2758,7 +2792,6 @@ func typeptrdata(t *rtype) uintptr { default: panic("reflect.typeptrdata: unexpected type, " + t.String()) } - return 0 } // See cmd/compile/internal/gc/reflect.go for derivation of constant. diff --git a/src/regexp/syntax/doc.go b/src/regexp/syntax/doc.go index e6c2ce5940ac2e..efc0b435711f3c 100644 --- a/src/regexp/syntax/doc.go +++ b/src/regexp/syntax/doc.go @@ -66,7 +66,7 @@ Grouping: Empty strings: ^ at beginning of text or line (flag m=true) - $ at end of text (like \z not \Z) or line (flag m=true) + $ at end of text (like \z not Perl's \Z) or line (flag m=true) \A at beginning of text \b at ASCII word boundary (\w on one side and \W, \A, or \z on the other) \B not at ASCII word boundary diff --git a/src/run.bash b/src/run.bash index 3acf46a9961a2d..293b775efa5e77 100755 --- a/src/run.bash +++ b/src/run.bash @@ -11,6 +11,7 @@ export GOROOT # the api test requires GOROOT to be set. unset CDPATH # in case user has it set unset GOPATH # we disallow local import for non-local packages, if $GOROOT happens # to be under $GOPATH, then some tests below will fail +unset GOBIN # Issue 14340 export GOHOSTOS export CC diff --git a/src/run.bat b/src/run.bat index 01a66bc574876a..6e42922a86e594 100644 --- a/src/run.bat +++ b/src/run.bat @@ -15,6 +15,8 @@ set GOBUILDFAIL=0 :: we disallow local import for non-local packages, if %GOROOT% happens :: to be under %GOPATH%, then some tests below will fail set GOPATH= +:: Issue 14340: ignore GOBIN during all.bat. +set GOBIN= rem TODO avoid rebuild if possible diff --git a/src/run.rc b/src/run.rc index d314808f3f28a7..88d77912e31efc 100755 --- a/src/run.rc +++ b/src/run.rc @@ -9,5 +9,6 @@ eval `{go env} GOPATH = () # we disallow local import for non-local packages, if $GOROOT happens # to be under $GOPATH, then some tests below will fail +GOBIN = () # Issue 14340 exec go tool dist test -rebuild $* diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go index cd28e3dca6933c..6b8968e382d349 100644 --- a/src/runtime/append_test.go +++ b/src/runtime/append_test.go @@ -3,7 +3,10 @@ // license that can be found in the LICENSE file. package runtime_test -import "testing" +import ( + "fmt" + "testing" +) const N = 20 @@ -84,75 +87,37 @@ func BenchmarkAppendGrowString(b *testing.B) { } } -func benchmarkAppendBytes(b *testing.B, length int) { - b.StopTimer() - x := make([]byte, 0, N) - y := make([]byte, length) - b.StartTimer() - for i := 0; i < b.N; i++ { - x = x[0:0] - x = append(x, y...) +func BenchmarkAppendSlice(b *testing.B) { + for _, length := range []int{1, 4, 7, 8, 15, 16, 32} { + b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) { + x := make([]byte, 0, N) + y := make([]byte, length) + for i := 0; i < b.N; i++ { + x = x[0:0] + x = append(x, y...) + } + }) } } -func BenchmarkAppend1Byte(b *testing.B) { - benchmarkAppendBytes(b, 1) -} - -func BenchmarkAppend4Bytes(b *testing.B) { - benchmarkAppendBytes(b, 4) -} - -func BenchmarkAppend7Bytes(b *testing.B) { - benchmarkAppendBytes(b, 7) -} - -func BenchmarkAppend8Bytes(b *testing.B) { - benchmarkAppendBytes(b, 8) -} - -func BenchmarkAppend15Bytes(b *testing.B) { - benchmarkAppendBytes(b, 15) -} - -func BenchmarkAppend16Bytes(b *testing.B) { - benchmarkAppendBytes(b, 16) -} - -func BenchmarkAppend32Bytes(b *testing.B) { - benchmarkAppendBytes(b, 32) -} - -func benchmarkAppendStr(b *testing.B, str string) { - b.StopTimer() - x := make([]byte, 0, N) - b.StartTimer() - for i := 0; i < b.N; i++ { - x = x[0:0] - x = append(x, str...) +func BenchmarkAppendStr(b *testing.B) { + for _, str := range []string{ + "1", + "1234", + "12345678", + "1234567890123456", + "12345678901234567890123456789012", + } { + b.Run(fmt.Sprint(len(str), "Bytes"), func(b *testing.B) { + x := make([]byte, 0, N) + for i := 0; i < b.N; i++ { + x = x[0:0] + x = append(x, str...) + } + }) } } -func BenchmarkAppendStr1Byte(b *testing.B) { - benchmarkAppendStr(b, "1") -} - -func BenchmarkAppendStr4Bytes(b *testing.B) { - benchmarkAppendStr(b, "1234") -} - -func BenchmarkAppendStr8Bytes(b *testing.B) { - benchmarkAppendStr(b, "12345678") -} - -func BenchmarkAppendStr16Bytes(b *testing.B) { - benchmarkAppendStr(b, "1234567890123456") -} - -func BenchmarkAppendStr32Bytes(b *testing.B) { - benchmarkAppendStr(b, "12345678901234567890123456789012") -} - func BenchmarkAppendSpecialCase(b *testing.B) { b.StopTimer() x := make([]int, 0, N) @@ -195,46 +160,28 @@ func TestAppendOverlap(t *testing.T) { } } -func benchmarkCopySlice(b *testing.B, l int) { - s := make([]byte, l) - buf := make([]byte, 4096) - var n int - for i := 0; i < b.N; i++ { - n = copy(buf, s) - } - b.SetBytes(int64(n)) -} - -func benchmarkCopyStr(b *testing.B, l int) { - s := string(make([]byte, l)) - buf := make([]byte, 4096) - var n int - for i := 0; i < b.N; i++ { - n = copy(buf, s) +func BenchmarkCopy(b *testing.B) { + for _, l := range []int{1, 2, 4, 8, 12, 16, 32, 128, 1024} { + buf := make([]byte, 4096) + b.Run(fmt.Sprint(l, "Byte"), func(b *testing.B) { + s := make([]byte, l) + var n int + for i := 0; i < b.N; i++ { + n = copy(buf, s) + } + b.SetBytes(int64(n)) + }) + b.Run(fmt.Sprint(l, "String"), func(b *testing.B) { + s := string(make([]byte, l)) + var n int + for i := 0; i < b.N; i++ { + n = copy(buf, s) + } + b.SetBytes(int64(n)) + }) } - b.SetBytes(int64(n)) } -func BenchmarkCopy1Byte(b *testing.B) { benchmarkCopySlice(b, 1) } -func BenchmarkCopy2Byte(b *testing.B) { benchmarkCopySlice(b, 2) } -func BenchmarkCopy4Byte(b *testing.B) { benchmarkCopySlice(b, 4) } -func BenchmarkCopy8Byte(b *testing.B) { benchmarkCopySlice(b, 8) } -func BenchmarkCopy12Byte(b *testing.B) { benchmarkCopySlice(b, 12) } -func BenchmarkCopy16Byte(b *testing.B) { benchmarkCopySlice(b, 16) } -func BenchmarkCopy32Byte(b *testing.B) { benchmarkCopySlice(b, 32) } -func BenchmarkCopy128Byte(b *testing.B) { benchmarkCopySlice(b, 128) } -func BenchmarkCopy1024Byte(b *testing.B) { benchmarkCopySlice(b, 1024) } - -func BenchmarkCopy1String(b *testing.B) { benchmarkCopyStr(b, 1) } -func BenchmarkCopy2String(b *testing.B) { benchmarkCopyStr(b, 2) } -func BenchmarkCopy4String(b *testing.B) { benchmarkCopyStr(b, 4) } -func BenchmarkCopy8String(b *testing.B) { benchmarkCopyStr(b, 8) } -func BenchmarkCopy12String(b *testing.B) { benchmarkCopyStr(b, 12) } -func BenchmarkCopy16String(b *testing.B) { benchmarkCopyStr(b, 16) } -func BenchmarkCopy32String(b *testing.B) { benchmarkCopyStr(b, 32) } -func BenchmarkCopy128String(b *testing.B) { benchmarkCopyStr(b, 128) } -func BenchmarkCopy1024String(b *testing.B) { benchmarkCopyStr(b, 1024) } - var ( sByte []byte s1Ptr []uintptr diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 6cd31f951bda74..e50c44304441de 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -526,6 +526,7 @@ TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16 MOVQ fv+0(FP), DX // fn MOVQ argp+8(FP), BX // caller sp LEAQ -8(BX), SP // caller sp after CALL + MOVQ -8(SP), BP // restore BP as if deferreturn returned (harmless if framepointers not in use) SUBQ $5, (SP) // return to CALL again MOVQ 0(DX), BX JMP BX // but first run the deferred function @@ -1800,6 +1801,7 @@ loop16: CMPQ DI,DX JB loop16 JMP fail +//TODO: the code below is wrong. Fix it. See #15679. _17_to_31: LEAQ 1(DI)(DX*1), DX SUBQ AX, DX diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go index 4fb4a613e044d4..9cf7b58a2f7a2e 100644 --- a/src/runtime/cgo.go +++ b/src/runtime/cgo.go @@ -11,8 +11,6 @@ import "unsafe" // Filled in by runtime/cgo when linked into binary. //go:linkname _cgo_init _cgo_init -//go:linkname _cgo_malloc _cgo_malloc -//go:linkname _cgo_free _cgo_free //go:linkname _cgo_thread_start _cgo_thread_start //go:linkname _cgo_sys_thread_create _cgo_sys_thread_create //go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done @@ -21,8 +19,6 @@ import "unsafe" var ( _cgo_init unsafe.Pointer - _cgo_malloc unsafe.Pointer - _cgo_free unsafe.Pointer _cgo_thread_start unsafe.Pointer _cgo_sys_thread_create unsafe.Pointer _cgo_notify_runtime_init_done unsafe.Pointer diff --git a/src/runtime/cgo/asm_arm.s b/src/runtime/cgo/asm_arm.s index 08472b6ab76ad9..0f354220bbf0b7 100644 --- a/src/runtime/cgo/asm_arm.s +++ b/src/runtime/cgo/asm_arm.s @@ -16,8 +16,40 @@ TEXT crosscall2(SB),NOSPLIT,$-4 * Additionally, runtime·load_g will clobber R0, so we need to save R0 * nevertheless. */ + SUB $(8*9), R13 // Reserve space for the floating point registers. MOVM.WP [R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, g, R11, R12, R14], (R13) + + // Skip floating point registers on GOARM < 6. + MOVB runtime·goarm(SB), R11 + CMP $6, R11 + BLT skipfpsave + MOVD F8, (14*4+8*1)(R13) + MOVD F9, (14*4+8*2)(R13) + MOVD F10, (14*4+8*3)(R13) + MOVD F11, (14*4+8*4)(R13) + MOVD F12, (14*4+8*5)(R13) + MOVD F13, (14*4+8*6)(R13) + MOVD F14, (14*4+8*7)(R13) + MOVD F15, (14*4+8*8)(R13) + +skipfpsave: BL runtime·load_g(SB) MOVW R15, R14 // R15 is PC. MOVW 0(R13), R15 - MOVM.IAW (R13), [R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, g, R11, R12, R15] + + MOVB runtime·goarm(SB), R11 + CMP $6, R11 + BLT skipfprest + MOVD (14*4+8*1)(R13), F8 + MOVD (14*4+8*2)(R13), F9 + MOVD (14*4+8*3)(R13), F10 + MOVD (14*4+8*4)(R13), F11 + MOVD (14*4+8*5)(R13), F12 + MOVD (14*4+8*6)(R13), F13 + MOVD (14*4+8*7)(R13), F14 + MOVD (14*4+8*8)(R13), F15 + +skipfprest: + MOVM.IAW (R13), [R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, g, R11, R12, R14] + ADD $(8*9), R13 + MOVW R14, R15 diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go index d0f63fb4ff9d74..9bde5a933fcd15 100644 --- a/src/runtime/cgo/callbacks.go +++ b/src/runtime/cgo/callbacks.go @@ -52,18 +52,6 @@ func _cgo_panic(a unsafe.Pointer, n int32) { var x_cgo_init byte var _cgo_init = &x_cgo_init -//go:cgo_import_static x_cgo_malloc -//go:linkname x_cgo_malloc x_cgo_malloc -//go:linkname _cgo_malloc _cgo_malloc -var x_cgo_malloc byte -var _cgo_malloc = &x_cgo_malloc - -//go:cgo_import_static x_cgo_free -//go:linkname x_cgo_free x_cgo_free -//go:linkname _cgo_free _cgo_free -var x_cgo_free byte -var _cgo_free = &x_cgo_free - //go:cgo_import_static x_cgo_thread_start //go:linkname x_cgo_thread_start x_cgo_thread_start //go:linkname _cgo_thread_start _cgo_thread_start diff --git a/src/runtime/cgo/gcc_openbsd_386.c b/src/runtime/cgo/gcc_openbsd_386.c index 22941a4c6d458f..1bc61ff7087bf9 100644 --- a/src/runtime/cgo/gcc_openbsd_386.c +++ b/src/runtime/cgo/gcc_openbsd_386.c @@ -13,9 +13,15 @@ static void* threadentry(void*); static void (*setg_gcc)(void*); -// TCB_SIZE is sizeof(struct thread_control_block), -// as defined in /usr/src/lib/librthread/tcb.h +// TCB_SIZE is sizeof(struct thread_control_block), as defined in +// /usr/src/lib/librthread/tcb.h on OpenBSD 5.9 and earlier. #define TCB_SIZE (4 * sizeof(void *)) + +// TIB_SIZE is sizeof(struct tib), as defined in +// /usr/include/tib.h on OpenBSD 6.0 and later. +#define TIB_SIZE (4 * sizeof(void *) + 6 * sizeof(int)) + +// TLS_SIZE is the size of TLS needed for Go. #define TLS_SIZE (2 * sizeof(void *)) void *__get_tcb(void); @@ -29,25 +35,38 @@ struct thread_args { void *arg; }; +static int has_tib = 0; + static void tcb_fixup(int mainthread) { - void *newtcb, *oldtcb; + void *tls, *newtcb, *oldtcb; + size_t tls_size, tcb_size; + + // TODO(jsing): Remove once OpenBSD 6.1 is released and OpenBSD 5.9 is + // no longer supported. // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result, // we need to allocate our own TLS space while preserving the existing - // TCB that has been setup via librthread. + // TCB or TIB that has been setup via librthread. - newtcb = malloc(TCB_SIZE + TLS_SIZE); - if(newtcb == NULL) + tcb_size = has_tib ? TIB_SIZE : TCB_SIZE; + tls_size = TLS_SIZE + tcb_size; + tls = malloc(tls_size); + if(tls == NULL) abort(); // The signal trampoline expects the TLS slots to be zeroed. - bzero(newtcb, TLS_SIZE); + bzero(tls, TLS_SIZE); oldtcb = __get_tcb(); - bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE); - __set_tcb(newtcb + TLS_SIZE); + newtcb = tls + TLS_SIZE; + bcopy(oldtcb, newtcb, tcb_size); + if(has_tib) { + // Fix up self pointer. + *(uintptr_t *)(newtcb) = (uintptr_t)newtcb; + } + __set_tcb(newtcb); // NOTE(jsing, minux): we can't free oldtcb without causing double-free // problem. so newtcb will be memory leaks. Get rid of this when OpenBSD @@ -79,6 +98,10 @@ static void init_pthread_wrapper(void) { fprintf(stderr, "runtime/cgo: dlsym failed to find pthread_create: %s\n", dlerror()); abort(); } + // _rthread_init is hidden in OpenBSD librthread that has TIB. + if(dlsym(handle, "_rthread_init") == NULL) { + has_tib = 1; + } dlclose(handle); } @@ -144,6 +167,7 @@ _cgo_sys_thread_start(ThreadStart *ts) pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstack will do the rest. ts->g->stackhi = size; err = sys_pthread_create(&p, &attr, threadentry, ts); diff --git a/src/runtime/cgo/gcc_openbsd_amd64.c b/src/runtime/cgo/gcc_openbsd_amd64.c index e84fe6c18b035f..4d4d14314c790b 100644 --- a/src/runtime/cgo/gcc_openbsd_amd64.c +++ b/src/runtime/cgo/gcc_openbsd_amd64.c @@ -13,9 +13,15 @@ static void* threadentry(void*); static void (*setg_gcc)(void*); -// TCB_SIZE is sizeof(struct thread_control_block), -// as defined in /usr/src/lib/librthread/tcb.h +// TCB_SIZE is sizeof(struct thread_control_block), as defined in +// /usr/src/lib/librthread/tcb.h on OpenBSD 5.9 and earlier. #define TCB_SIZE (4 * sizeof(void *)) + +// TIB_SIZE is sizeof(struct tib), as defined in +// /usr/include/tib.h on OpenBSD 6.0 and later. +#define TIB_SIZE (4 * sizeof(void *) + 6 * sizeof(int)) + +// TLS_SIZE is the size of TLS needed for Go. #define TLS_SIZE (2 * sizeof(void *)) void *__get_tcb(void); @@ -29,25 +35,38 @@ struct thread_args { void *arg; }; +static int has_tib = 0; + static void tcb_fixup(int mainthread) { - void *newtcb, *oldtcb; + void *tls, *newtcb, *oldtcb; + size_t tls_size, tcb_size; + + // TODO(jsing): Remove once OpenBSD 6.1 is released and OpenBSD 5.9 is + // no longer supported. // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result, // we need to allocate our own TLS space while preserving the existing - // TCB that has been setup via librthread. + // TCB or TIB that has been setup via librthread. - newtcb = malloc(TCB_SIZE + TLS_SIZE); - if(newtcb == NULL) + tcb_size = has_tib ? TIB_SIZE : TCB_SIZE; + tls_size = TLS_SIZE + tcb_size; + tls = malloc(tls_size); + if(tls == NULL) abort(); // The signal trampoline expects the TLS slots to be zeroed. - bzero(newtcb, TLS_SIZE); + bzero(tls, TLS_SIZE); oldtcb = __get_tcb(); - bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE); - __set_tcb(newtcb + TLS_SIZE); + newtcb = tls + TLS_SIZE; + bcopy(oldtcb, newtcb, tcb_size); + if(has_tib) { + // Fix up self pointer. + *(uintptr_t *)(newtcb) = (uintptr_t)newtcb; + } + __set_tcb(newtcb); // NOTE(jsing, minux): we can't free oldtcb without causing double-free // problem. so newtcb will be memory leaks. Get rid of this when OpenBSD @@ -79,6 +98,10 @@ static void init_pthread_wrapper(void) { fprintf(stderr, "runtime/cgo: dlsym failed to find pthread_create: %s\n", dlerror()); abort(); } + // _rthread_init is hidden in OpenBSD librthread that has TIB. + if(dlsym(handle, "_rthread_init") == NULL) { + has_tib = 1; + } dlclose(handle); } diff --git a/src/runtime/cgo/gcc_util.c b/src/runtime/cgo/gcc_util.c index e20d206be6d799..4111fe11951df8 100644 --- a/src/runtime/cgo/gcc_util.c +++ b/src/runtime/cgo/gcc_util.c @@ -4,31 +4,6 @@ #include "libcgo.h" -/* Stub for calling malloc from Go */ -void -x_cgo_malloc(void *p) -{ - struct a { - long long n; - void *ret; - } *a = p; - - a->ret = malloc(a->n); - if(a->ret == NULL && a->n == 0) - a->ret = malloc(1); -} - -/* Stub for calling free from Go */ -void -x_cgo_free(void *p) -{ - struct a { - void *arg; - } *a = p; - - free(a->arg); -} - /* Stub for creating a new thread */ void x_cgo_thread_start(ThreadStart *arg) diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 8457fb2de73c71..0f8386b10f54d5 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -145,25 +145,6 @@ func endcgo(mp *m) { unlockOSThread() // invalidates mp } -// Helper functions for cgo code. - -func cmalloc(n uintptr) unsafe.Pointer { - var args struct { - n uint64 - ret unsafe.Pointer - } - args.n = uint64(n) - cgocall(_cgo_malloc, unsafe.Pointer(&args)) - if args.ret == nil { - throw("C malloc failed") - } - return args.ret -} - -func cfree(p unsafe.Pointer) { - cgocall(_cgo_free, p) -} - // Call from C back to Go. //go:nosplit func cgocallbackg(ctxt uintptr) { @@ -601,7 +582,7 @@ func cgoIsGoPointer(p unsafe.Pointer) bool { return false } - if cgoInRange(p, mheap_.arena_start, mheap_.arena_used) { + if inHeapOrStack(uintptr(p)) { return true } diff --git a/src/runtime/cgocheck.go b/src/runtime/cgocheck.go index d85d5fe5a8ba15..2d064145a415de 100644 --- a/src/runtime/cgocheck.go +++ b/src/runtime/cgocheck.go @@ -94,6 +94,14 @@ func cgoCheckSliceCopy(typ *_type, dst, src slice, n int) { //go:nosplit //go:nowritebarrier func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { + // Anything past typ.ptrdata is not a pointer. + if typ.ptrdata <= off { + return + } + if ptrdataSize := typ.ptrdata - off; size > ptrdataSize { + size = ptrdataSize + } + if typ.kind&kindGCProg == 0 { cgoCheckBits(src, typ.gcdata, off, size) return @@ -184,7 +192,7 @@ func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) { // cgoCheckUsingType is like cgoCheckTypedBlock, but is a last ditch // fall back to look for pointers in src using the type information. -// We only this when looking at a value on the stack when the type +// We only use this when looking at a value on the stack when the type // uses a GC program, because otherwise it's more efficient to use the // GC bits. This is called on the system stack. //go:nowritebarrier @@ -193,6 +201,15 @@ func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) { if typ.kind&kindNoPointers != 0 { return } + + // Anything past typ.ptrdata is not a pointer. + if typ.ptrdata <= off { + return + } + if ptrdataSize := typ.ptrdata - off; size > ptrdataSize { + size = ptrdataSize + } + if typ.kind&kindGCProg == 0 { cgoCheckBits(src, typ.gcdata, off, size) return diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go index 771b303f6ee39a..0a79661f1ead83 100644 --- a/src/runtime/crash_unix_test.go +++ b/src/runtime/crash_unix_test.go @@ -149,10 +149,6 @@ func loop(i int, c chan bool) { func TestSignalExitStatus(t *testing.T) { testenv.MustHaveGoBuild(t) - switch runtime.GOOS { - case "netbsd", "solaris": - t.Skipf("skipping on %s; see https://golang.org/issue/14063", runtime.GOOS) - } exe, err := buildTestProg(t, "testprog") if err != nil { t.Fatal(err) diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index bb17919fd0135d..b079a07d518456 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -170,7 +170,7 @@ const ( _MaxGcproc = 32 ) -const _MaxArena32 = 2 << 30 +const _MaxArena32 = 1<<32 - 1 // OS-defined helpers: // @@ -227,7 +227,7 @@ func mallocinit() { // Set up the allocation arena, a contiguous area of memory where // allocated data will be found. The arena begins with a bitmap large - // enough to hold 4 bits per allocated word. + // enough to hold 2 bits per allocated word. if sys.PtrSize == 8 && (limit == 0 || limit > 1<<30) { // On a 64-bit machine, allocate from a single contiguous reservation. // 512 GB (MaxMem) should be big enough for now. @@ -259,7 +259,7 @@ func mallocinit() { // translation buffers, the user address space is limited to 39 bits // On darwin/arm64, the address space is even smaller. arenaSize := round(_MaxMem, _PageSize) - bitmapSize = arenaSize / (sys.PtrSize * 8 / 4) + bitmapSize = arenaSize / (sys.PtrSize * 8 / 2) spansSize = arenaSize / _PageSize * sys.PtrSize spansSize = round(spansSize, _PageSize) for i := 0; i <= 0x7f; i++ { @@ -284,32 +284,26 @@ func mallocinit() { // with a giant virtual address space reservation. // Instead we map the memory information bitmap // immediately after the data segment, large enough - // to handle another 2GB of mappings (256 MB), + // to handle the entire 4GB address space (256 MB), // along with a reservation for an initial arena. // When that gets used up, we'll start asking the kernel - // for any memory anywhere and hope it's in the 2GB - // following the bitmap (presumably the executable begins - // near the bottom of memory, so we'll have to use up - // most of memory before the kernel resorts to giving out - // memory before the beginning of the text segment). - // - // Alternatively we could reserve 512 MB bitmap, enough - // for 4GB of mappings, and then accept any memory the - // kernel threw at us, but normally that's a waste of 512 MB - // of address space, which is probably too much in a 32-bit world. + // for any memory anywhere. // If we fail to allocate, try again with a smaller arena. // This is necessary on Android L where we share a process // with ART, which reserves virtual memory aggressively. + // In the worst case, fall back to a 0-sized initial arena, + // in the hope that subsequent reservations will succeed. arenaSizes := []uintptr{ 512 << 20, 256 << 20, 128 << 20, + 0, } for _, arenaSize := range arenaSizes { - bitmapSize = _MaxArena32 / (sys.PtrSize * 8 / 4) - spansSize = _MaxArena32 / _PageSize * sys.PtrSize + bitmapSize = (_MaxArena32 + 1) / (sys.PtrSize * 8 / 2) + spansSize = (_MaxArena32 + 1) / _PageSize * sys.PtrSize if limit > 0 && arenaSize+bitmapSize+spansSize > limit { bitmapSize = (limit / 9) &^ ((1 << _PageShift) - 1) arenaSize = bitmapSize * 8 @@ -344,10 +338,16 @@ func mallocinit() { p1 := round(p, _PageSize) mheap_.spans = (**mspan)(unsafe.Pointer(p1)) - mheap_.bitmap = p1 + spansSize - mheap_.arena_start = p1 + (spansSize + bitmapSize) - mheap_.arena_used = mheap_.arena_start + mheap_.bitmap = p1 + spansSize + bitmapSize + if sys.PtrSize == 4 { + // Set arena_start such that we can accept memory + // reservations located anywhere in the 4GB virtual space. + mheap_.arena_start = 0 + } else { + mheap_.arena_start = p1 + (spansSize + bitmapSize) + } mheap_.arena_end = p + pSize + mheap_.arena_used = p1 + (spansSize + bitmapSize) mheap_.arena_reserved = reserved if mheap_.arena_start&(_PageSize-1) != 0 { @@ -361,29 +361,6 @@ func mallocinit() { _g_.m.mcache = allocmcache() } -// sysReserveHigh reserves space somewhere high in the address space. -// sysReserve doesn't actually reserve the full amount requested on -// 64-bit systems, because of problems with ulimit. Instead it checks -// that it can get the first 64 kB and assumes it can grab the rest as -// needed. This doesn't work well with the "let the kernel pick an address" -// mode, so don't do that. Pick a high address instead. -func sysReserveHigh(n uintptr, reserved *bool) unsafe.Pointer { - if sys.PtrSize == 4 { - return sysReserve(nil, n, reserved) - } - - for i := 0; i <= 0x7f; i++ { - p := uintptr(i)<<40 | uintptrMask&(0x00c0<<32) - *reserved = false - p = uintptr(sysReserve(unsafe.Pointer(p), n, reserved)) - if p != 0 { - return unsafe.Pointer(p) - } - } - - return sysReserve(nil, n, reserved) -} - // sysAlloc allocates the next n bytes from the heap arena. The // returned pointer is always _PageSize aligned and between // h.arena_start and h.arena_end. sysAlloc returns nil on failure. @@ -394,7 +371,7 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer { // Reserve some more space. p_size := round(n+_PageSize, 256<<20) new_end := h.arena_end + p_size // Careful: can overflow - if h.arena_end <= new_end && new_end <= h.arena_start+_MaxArena32 { + if h.arena_end <= new_end && new_end-h.arena_start-1 <= _MaxArena32 { // TODO: It would be bad if part of the arena // is reserved and part is not. var reserved bool @@ -405,7 +382,7 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer { if p == h.arena_end { h.arena_end = new_end h.arena_reserved = reserved - } else if h.arena_start <= p && p+p_size <= h.arena_start+_MaxArena32 { + } else if h.arena_start <= p && p+p_size-h.arena_start-1 <= _MaxArena32 { // Keep everything page-aligned. // Our pages are bigger than hardware pages. h.arena_end = p + p_size @@ -442,23 +419,22 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer { } // If using 64-bit, our reservation is all we have. - if h.arena_end-h.arena_start >= _MaxArena32 { + if h.arena_end-h.arena_start > _MaxArena32 { return nil } // On 32-bit, once the reservation is gone we can - // try to get memory at a location chosen by the OS - // and hope that it is in the range we allocated bitmap for. + // try to get memory at a location chosen by the OS. p_size := round(n, _PageSize) + _PageSize p := uintptr(sysAlloc(p_size, &memstats.heap_sys)) if p == 0 { return nil } - if p < h.arena_start || p+p_size-h.arena_start >= _MaxArena32 { + if p < h.arena_start || p+p_size-h.arena_start > _MaxArena32 { top := ^uintptr(0) - if top-h.arena_start > _MaxArena32 { - top = h.arena_start + _MaxArena32 + if top-h.arena_start-1 > _MaxArena32 { + top = h.arena_start + _MaxArena32 + 1 } print("runtime: memory allocated by OS (", hex(p), ") not in usable range [", hex(h.arena_start), ",", hex(top), ")\n") sysFree(unsafe.Pointer(p), p_size, &memstats.heap_sys) @@ -723,16 +699,16 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { scanSize = typ.ptrdata } c.local_scan += scanSize - - // Ensure that the stores above that initialize x to - // type-safe memory and set the heap bits occur before - // the caller can make x observable to the garbage - // collector. Otherwise, on weakly ordered machines, - // the garbage collector could follow a pointer to x, - // but see uninitialized memory or stale heap bits. - publicationBarrier() } + // Ensure that the stores above that initialize x to + // type-safe memory and set the heap bits occur before + // the caller can make x observable to the garbage + // collector. Otherwise, on weakly ordered machines, + // the garbage collector could follow a pointer to x, + // but see uninitialized memory or stale heap bits. + publicationBarrier() + // Allocate black during GC. // All slots hold nil so no scanning is needed. // This may be racing with GC so do it atomically if there can be diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index cdb36cd65145bd..ccefbcd8d62df2 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -156,7 +156,7 @@ func (h *mheap) mapBits(arena_used uintptr) { return } - sysMap(unsafe.Pointer(h.arena_start-n), n-h.bitmap_mapped, h.arena_reserved, &memstats.gc_sys) + sysMap(unsafe.Pointer(h.bitmap-n), n-h.bitmap_mapped, h.arena_reserved, &memstats.gc_sys) h.bitmap_mapped = n } @@ -364,7 +364,7 @@ func (m *markBits) advance() { func heapBitsForAddr(addr uintptr) heapBits { // 2 bits per work, 4 pairs per byte, and a mask is hard coded. off := (addr - mheap_.arena_start) / sys.PtrSize - return heapBits{(*uint8)(unsafe.Pointer(mheap_.arena_start - off/4 - 1)), uint32(off & 3)} + return heapBits{(*uint8)(unsafe.Pointer(mheap_.bitmap - off/4 - 1)), uint32(off & 3)} } // heapBitsForSpan returns the heapBits for the span base address base. @@ -498,7 +498,6 @@ func (h heapBits) morePointers() bool { } // isPointer reports whether the heap bits describe a pointer word. -// h must describe the initial word of the object. // // nosplit because it is used during write barriers and must not be preempted. //go:nosplit @@ -507,8 +506,7 @@ func (h heapBits) isPointer() bool { } // hasPointers reports whether the given object has any pointers. -// It must be told how large the object at h is, so that it does not read too -// far into the bitmap. +// It must be told how large the object at h is for efficiency. // h must describe the initial word of the object. func (h heapBits) hasPointers(size uintptr) bool { if size == sys.PtrSize { // 1-word objects are always pointers @@ -849,10 +847,20 @@ func (s *mspan) countFree() int { // malloc does not call heapBitsSetType when there are no pointers, // because all free objects are marked as noscan during // heapBitsSweepSpan. +// // There can only be one allocation from a given span active at a time, -// so this code is not racing with other instances of itself, and -// the bitmap for a span always falls on byte boundaries. -// Hence, it can access the bitmap with racing. +// and the bitmap for a span always falls on byte boundaries, +// so there are no write-write races for access to the heap bitmap. +// Hence, heapBitsSetType can access the bitmap without atomics. +// +// There can be read-write races between heapBitsSetType and things +// that read the heap bitmap like scanobject. However, since +// heapBitsSetType is only used for objects that have not yet been +// made reachable, readers will ignore bits being modified by this +// function. This does mean this function cannot transiently modify +// bits that belong to neighboring objects. Also, on weakly-ordered +// machines, callers must execute a store/store (publication) barrier +// between calling this function and making the object reachable. // // TODO: This still has atomic accesses left over from when it could // race with GC accessing mark bits in the bitmap. Remove these. diff --git a/src/runtime/memmove_test.go b/src/runtime/memmove_test.go index 8bf0c65e298329..2124cb9d499d50 100644 --- a/src/runtime/memmove_test.go +++ b/src/runtime/memmove_test.go @@ -5,6 +5,7 @@ package runtime_test import ( + "fmt" . "runtime" "testing" ) @@ -81,110 +82,49 @@ func TestMemmoveAlias(t *testing.T) { } } -func bmMemmove(b *testing.B, n int) { - x := make([]byte, n) - y := make([]byte, n) - b.SetBytes(int64(n)) - for i := 0; i < b.N; i++ { - copy(x, y) +func benchmarkSizes(b *testing.B, sizes []int, fn func(b *testing.B, n int)) { + for _, n := range sizes { + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n)) + fn(b, n) + }) } } -func BenchmarkMemmove0(b *testing.B) { bmMemmove(b, 0) } -func BenchmarkMemmove1(b *testing.B) { bmMemmove(b, 1) } -func BenchmarkMemmove2(b *testing.B) { bmMemmove(b, 2) } -func BenchmarkMemmove3(b *testing.B) { bmMemmove(b, 3) } -func BenchmarkMemmove4(b *testing.B) { bmMemmove(b, 4) } -func BenchmarkMemmove5(b *testing.B) { bmMemmove(b, 5) } -func BenchmarkMemmove6(b *testing.B) { bmMemmove(b, 6) } -func BenchmarkMemmove7(b *testing.B) { bmMemmove(b, 7) } -func BenchmarkMemmove8(b *testing.B) { bmMemmove(b, 8) } -func BenchmarkMemmove9(b *testing.B) { bmMemmove(b, 9) } -func BenchmarkMemmove10(b *testing.B) { bmMemmove(b, 10) } -func BenchmarkMemmove11(b *testing.B) { bmMemmove(b, 11) } -func BenchmarkMemmove12(b *testing.B) { bmMemmove(b, 12) } -func BenchmarkMemmove13(b *testing.B) { bmMemmove(b, 13) } -func BenchmarkMemmove14(b *testing.B) { bmMemmove(b, 14) } -func BenchmarkMemmove15(b *testing.B) { bmMemmove(b, 15) } -func BenchmarkMemmove16(b *testing.B) { bmMemmove(b, 16) } -func BenchmarkMemmove32(b *testing.B) { bmMemmove(b, 32) } -func BenchmarkMemmove64(b *testing.B) { bmMemmove(b, 64) } -func BenchmarkMemmove128(b *testing.B) { bmMemmove(b, 128) } -func BenchmarkMemmove256(b *testing.B) { bmMemmove(b, 256) } -func BenchmarkMemmove512(b *testing.B) { bmMemmove(b, 512) } -func BenchmarkMemmove1024(b *testing.B) { bmMemmove(b, 1024) } -func BenchmarkMemmove2048(b *testing.B) { bmMemmove(b, 2048) } -func BenchmarkMemmove4096(b *testing.B) { bmMemmove(b, 4096) } - -func bmMemmoveUnalignedDst(b *testing.B, n int) { - x := make([]byte, n+1) - y := make([]byte, n) - b.SetBytes(int64(n)) - for i := 0; i < b.N; i++ { - copy(x[1:], y) - } +var bufSizes = []int{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 32, 64, 128, 256, 512, 1024, 2048, 4096, } -func BenchmarkMemmoveUnalignedDst0(b *testing.B) { bmMemmoveUnalignedDst(b, 0) } -func BenchmarkMemmoveUnalignedDst1(b *testing.B) { bmMemmoveUnalignedDst(b, 1) } -func BenchmarkMemmoveUnalignedDst2(b *testing.B) { bmMemmoveUnalignedDst(b, 2) } -func BenchmarkMemmoveUnalignedDst3(b *testing.B) { bmMemmoveUnalignedDst(b, 3) } -func BenchmarkMemmoveUnalignedDst4(b *testing.B) { bmMemmoveUnalignedDst(b, 4) } -func BenchmarkMemmoveUnalignedDst5(b *testing.B) { bmMemmoveUnalignedDst(b, 5) } -func BenchmarkMemmoveUnalignedDst6(b *testing.B) { bmMemmoveUnalignedDst(b, 6) } -func BenchmarkMemmoveUnalignedDst7(b *testing.B) { bmMemmoveUnalignedDst(b, 7) } -func BenchmarkMemmoveUnalignedDst8(b *testing.B) { bmMemmoveUnalignedDst(b, 8) } -func BenchmarkMemmoveUnalignedDst9(b *testing.B) { bmMemmoveUnalignedDst(b, 9) } -func BenchmarkMemmoveUnalignedDst10(b *testing.B) { bmMemmoveUnalignedDst(b, 10) } -func BenchmarkMemmoveUnalignedDst11(b *testing.B) { bmMemmoveUnalignedDst(b, 11) } -func BenchmarkMemmoveUnalignedDst12(b *testing.B) { bmMemmoveUnalignedDst(b, 12) } -func BenchmarkMemmoveUnalignedDst13(b *testing.B) { bmMemmoveUnalignedDst(b, 13) } -func BenchmarkMemmoveUnalignedDst14(b *testing.B) { bmMemmoveUnalignedDst(b, 14) } -func BenchmarkMemmoveUnalignedDst15(b *testing.B) { bmMemmoveUnalignedDst(b, 15) } -func BenchmarkMemmoveUnalignedDst16(b *testing.B) { bmMemmoveUnalignedDst(b, 16) } -func BenchmarkMemmoveUnalignedDst32(b *testing.B) { bmMemmoveUnalignedDst(b, 32) } -func BenchmarkMemmoveUnalignedDst64(b *testing.B) { bmMemmoveUnalignedDst(b, 64) } -func BenchmarkMemmoveUnalignedDst128(b *testing.B) { bmMemmoveUnalignedDst(b, 128) } -func BenchmarkMemmoveUnalignedDst256(b *testing.B) { bmMemmoveUnalignedDst(b, 256) } -func BenchmarkMemmoveUnalignedDst512(b *testing.B) { bmMemmoveUnalignedDst(b, 512) } -func BenchmarkMemmoveUnalignedDst1024(b *testing.B) { bmMemmoveUnalignedDst(b, 1024) } -func BenchmarkMemmoveUnalignedDst2048(b *testing.B) { bmMemmoveUnalignedDst(b, 2048) } -func BenchmarkMemmoveUnalignedDst4096(b *testing.B) { bmMemmoveUnalignedDst(b, 4096) } +func BenchmarkMemmove(b *testing.B) { + benchmarkSizes(b, bufSizes, func(b *testing.B, n int) { + x := make([]byte, n) + y := make([]byte, n) + for i := 0; i < b.N; i++ { + copy(x, y) + } + }) +} -func bmMemmoveUnalignedSrc(b *testing.B, n int) { - x := make([]byte, n) - y := make([]byte, n+1) - b.SetBytes(int64(n)) - for i := 0; i < b.N; i++ { - copy(x, y[1:]) - } +func BenchmarkMemmoveUnalignedDst(b *testing.B) { + benchmarkSizes(b, bufSizes, func(b *testing.B, n int) { + x := make([]byte, n+1) + y := make([]byte, n) + for i := 0; i < b.N; i++ { + copy(x[1:], y) + } + }) } -func BenchmarkMemmoveUnalignedSrc0(b *testing.B) { bmMemmoveUnalignedSrc(b, 0) } -func BenchmarkMemmoveUnalignedSrc1(b *testing.B) { bmMemmoveUnalignedSrc(b, 1) } -func BenchmarkMemmoveUnalignedSrc2(b *testing.B) { bmMemmoveUnalignedSrc(b, 2) } -func BenchmarkMemmoveUnalignedSrc3(b *testing.B) { bmMemmoveUnalignedSrc(b, 3) } -func BenchmarkMemmoveUnalignedSrc4(b *testing.B) { bmMemmoveUnalignedSrc(b, 4) } -func BenchmarkMemmoveUnalignedSrc5(b *testing.B) { bmMemmoveUnalignedSrc(b, 5) } -func BenchmarkMemmoveUnalignedSrc6(b *testing.B) { bmMemmoveUnalignedSrc(b, 6) } -func BenchmarkMemmoveUnalignedSrc7(b *testing.B) { bmMemmoveUnalignedSrc(b, 7) } -func BenchmarkMemmoveUnalignedSrc8(b *testing.B) { bmMemmoveUnalignedSrc(b, 8) } -func BenchmarkMemmoveUnalignedSrc9(b *testing.B) { bmMemmoveUnalignedSrc(b, 9) } -func BenchmarkMemmoveUnalignedSrc10(b *testing.B) { bmMemmoveUnalignedSrc(b, 10) } -func BenchmarkMemmoveUnalignedSrc11(b *testing.B) { bmMemmoveUnalignedSrc(b, 11) } -func BenchmarkMemmoveUnalignedSrc12(b *testing.B) { bmMemmoveUnalignedSrc(b, 12) } -func BenchmarkMemmoveUnalignedSrc13(b *testing.B) { bmMemmoveUnalignedSrc(b, 13) } -func BenchmarkMemmoveUnalignedSrc14(b *testing.B) { bmMemmoveUnalignedSrc(b, 14) } -func BenchmarkMemmoveUnalignedSrc15(b *testing.B) { bmMemmoveUnalignedSrc(b, 15) } -func BenchmarkMemmoveUnalignedSrc16(b *testing.B) { bmMemmoveUnalignedSrc(b, 16) } -func BenchmarkMemmoveUnalignedSrc32(b *testing.B) { bmMemmoveUnalignedSrc(b, 32) } -func BenchmarkMemmoveUnalignedSrc64(b *testing.B) { bmMemmoveUnalignedSrc(b, 64) } -func BenchmarkMemmoveUnalignedSrc128(b *testing.B) { bmMemmoveUnalignedSrc(b, 128) } -func BenchmarkMemmoveUnalignedSrc256(b *testing.B) { bmMemmoveUnalignedSrc(b, 256) } -func BenchmarkMemmoveUnalignedSrc512(b *testing.B) { bmMemmoveUnalignedSrc(b, 512) } -func BenchmarkMemmoveUnalignedSrc1024(b *testing.B) { bmMemmoveUnalignedSrc(b, 1024) } -func BenchmarkMemmoveUnalignedSrc2048(b *testing.B) { bmMemmoveUnalignedSrc(b, 2048) } -func BenchmarkMemmoveUnalignedSrc4096(b *testing.B) { bmMemmoveUnalignedSrc(b, 4096) } +func BenchmarkMemmoveUnalignedSrc(b *testing.B) { + benchmarkSizes(b, bufSizes, func(b *testing.B, n int) { + x := make([]byte, n) + y := make([]byte, n+1) + for i := 0; i < b.N; i++ { + copy(x, y[1:]) + } + }) +} func TestMemclr(t *testing.T) { size := 512 @@ -218,38 +158,37 @@ func TestMemclr(t *testing.T) { } } -func bmMemclr(b *testing.B, n int) { - x := make([]byte, n) - b.SetBytes(int64(n)) - for i := 0; i < b.N; i++ { - MemclrBytes(x) - } -} -func BenchmarkMemclr5(b *testing.B) { bmMemclr(b, 5) } -func BenchmarkMemclr16(b *testing.B) { bmMemclr(b, 16) } -func BenchmarkMemclr64(b *testing.B) { bmMemclr(b, 64) } -func BenchmarkMemclr256(b *testing.B) { bmMemclr(b, 256) } -func BenchmarkMemclr4096(b *testing.B) { bmMemclr(b, 4096) } -func BenchmarkMemclr65536(b *testing.B) { bmMemclr(b, 65536) } -func BenchmarkMemclr1M(b *testing.B) { bmMemclr(b, 1<<20) } -func BenchmarkMemclr4M(b *testing.B) { bmMemclr(b, 4<<20) } -func BenchmarkMemclr8M(b *testing.B) { bmMemclr(b, 8<<20) } -func BenchmarkMemclr16M(b *testing.B) { bmMemclr(b, 16<<20) } -func BenchmarkMemclr64M(b *testing.B) { bmMemclr(b, 64<<20) } +func BenchmarkMemclr(b *testing.B) { + for _, n := range []int{5, 16, 64, 256, 4096, 65536} { + x := make([]byte, n) + b.Run(fmt.Sprint(n), func(b *testing.B) { + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + MemclrBytes(x) + } + }) + } + for _, m := range []int{1, 4, 8, 16, 64} { + x := make([]byte, m<<20) + b.Run(fmt.Sprint(m, "M"), func(b *testing.B) { + b.SetBytes(int64(m << 20)) + for i := 0; i < b.N; i++ { + MemclrBytes(x) + } + }) + } +} -func bmGoMemclr(b *testing.B, n int) { - x := make([]byte, n) - b.SetBytes(int64(n)) - for i := 0; i < b.N; i++ { - for j := range x { - x[j] = 0 +func BenchmarkGoMemclr(b *testing.B) { + benchmarkSizes(b, []int{5, 16, 64, 256}, func(b *testing.B, n int) { + x := make([]byte, n) + for i := 0; i < b.N; i++ { + for j := range x { + x[j] = 0 + } } - } + }) } -func BenchmarkGoMemclr5(b *testing.B) { bmGoMemclr(b, 5) } -func BenchmarkGoMemclr16(b *testing.B) { bmGoMemclr(b, 16) } -func BenchmarkGoMemclr64(b *testing.B) { bmGoMemclr(b, 64) } -func BenchmarkGoMemclr256(b *testing.B) { bmGoMemclr(b, 256) } func BenchmarkClearFat8(b *testing.B) { for i := 0; i < b.N; i++ { diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 6dce6d75011ec9..1a744e4a51e697 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -259,6 +259,24 @@ func runfinq() { // in initializers for package-level variables. Such objects may be // linker-allocated, not heap-allocated. // +// A finalizer may run as soon as an object becomes unreachable. +// In order to use finalizers correctly, the program must ensure that +// the object is reachable until it is no longer required. +// Objects stored in global variables, or that can be found by tracing +// pointers from a global variable, are reachable. For other objects, +// pass the object to a call of the KeepAlive function to mark the +// last point in the function where the object must be reachable. +// +// For example, if p points to a struct that contains a file descriptor d, +// and p has a finalizer that closes that file descriptor, and if the last +// use of p in a function is a call to syscall.Write(p.d, buf, size), then +// p may be unreachable as soon as the program enters syscall.Write. The +// finalizer may run at that moment, closing p.d, causing syscall.Write +// to fail because it is writing to a closed file descriptor (or, worse, +// to an entirely different file descriptor opened by a different goroutine). +// To avoid this problem, call runtime.KeepAlive(p) after the call to +// syscall.Write. +// // A single goroutine runs all finalizers for a program, sequentially. // If a finalizer must run for a long time, it should do so by starting // a new goroutine. @@ -416,3 +434,31 @@ func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) { } return } + +// Mark KeepAlive as noinline so that the current compiler will ensure +// that the argument is alive at the point of the function call. +// If it were inlined, it would disappear, and there would be nothing +// keeping the argument alive. Perhaps a future compiler will recognize +// runtime.KeepAlive specially and do something more efficient. +//go:noinline + +// KeepAlive marks its argument as currently reachable. +// This ensures that the object is not freed, and its finalizer is not run, +// before the point in the program where KeepAlive is called. +// +// A very simplified example showing where KeepAlive is required: +// type File struct { d int } +// d, err := syscall.Open("/file/path", syscall.O_RDONLY, 0) +// // ... do something if err != nil ... +// p := &FILE{d} +// runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) }) +// var buf [10]byte +// n, err := syscall.Read(p.d, buf[:]) +// // Ensure p is not finalized until Read returns. +// runtime.KeepAlive(p) +// // No more uses of p after this point. +// +// Without the KeepAlive call, the finalizer could run at the start of +// syscall.Read, closing the file descriptor before syscall.Read makes +// the actual system call. +func KeepAlive(interface{}) {} diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index c497ccee67e724..c50bd028810f0b 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1389,7 +1389,7 @@ func gcBgMarkWorker(_p_ *p) { notewakeup(&work.bgMarkReady) for { - // Go to sleep until woken by gcContoller.findRunnable. + // Go to sleep until woken by gcController.findRunnable. // We can't releasem yet since even the call to gopark // may be preempted. gopark(func(g *g, parkp unsafe.Pointer) bool { @@ -1705,7 +1705,7 @@ func gcSweep(mode gcMode) { lock(&sweep.lock) if sweep.parked { sweep.parked = false - ready(sweep.g, 0) + ready(sweep.g, 0, true) } unlock(&sweep.lock) mProf_GC() diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index af3205ab2332bf..00b96fd00beed2 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -174,7 +174,9 @@ func markroot(gcw *gcWork, i uint32) { // Only do this once per GC cycle; preferably // concurrently. if !work.markrootDone { - markrootFreeGStacks() + // Switch to the system stack so we can call + // stackfree. + systemstack(markrootFreeGStacks) } case baseSpans <= i && i < baseStacks: @@ -231,7 +233,7 @@ func markroot(gcw *gcWork, i uint32) { // we scan the stacks we can and ask running // goroutines to scan themselves; and the // second blocks. - scang(gp) + scang(gp, gcw) if selfScan { casgstatus(userG, _Gwaiting, _Grunning) @@ -601,7 +603,13 @@ func gcFlushBgCredit(scanWork int64) { gp.gcAssistBytes = 0 xgp := gp gp = gp.schedlink.ptr() - ready(xgp, 0) + // It's important that we *not* put xgp in + // runnext. Otherwise, it's possible for user + // code to exploit the GC worker's high + // scheduler priority to get itself always run + // before other goroutines and always in the + // fresh quantum started by GC. + ready(xgp, 0, false) } else { // Partially satisfy this assist. gp.gcAssistBytes += scanBytes @@ -636,8 +644,18 @@ func gcFlushBgCredit(scanWork int64) { unlock(&work.assistQueue.lock) } +// scanstack scans gp's stack, greying all pointers found on the stack. +// +// During mark phase, it also installs stack barriers while traversing +// gp's stack. During mark termination, it stops scanning when it +// reaches an unhit stack barrier. +// +// scanstack is marked go:systemstack because it must not be preempted +// while using a workbuf. +// //go:nowritebarrier -func scanstack(gp *g) { +//go:systemstack +func scanstack(gp *g, gcw *gcWork) { if gp.gcscanvalid { return } @@ -726,7 +744,6 @@ func scanstack(gp *g) { // Scan the stack. var cache pcvalueCache - gcw := &getg().m.p.ptr().gcw n := 0 scanframe := func(frame *stkframe, unused unsafe.Pointer) bool { scanframeworker(frame, &cache, gcw) @@ -754,9 +771,6 @@ func scanstack(gp *g) { } gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, scanframe, nil, 0) tracebackdefers(gp, scanframe, nil) - if gcphase == _GCmarktermination { - gcw.dispose() - } gcUnlockStackBarriers(gp) if gcphase == _GCmark { // gp may have added itself to the rescan list between @@ -1096,7 +1110,7 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) { // scanobject scans the object starting at b, adding pointers to gcw. // b must point to the beginning of a heap object; scanobject consults // the GC bitmap for the pointer mask and the spans for the size of the -// object (it ignores n). +// object. //go:nowritebarrier func scanobject(b uintptr, gcw *gcWork) { // Note that arena_used may change concurrently during diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 1f732c2111c975..4093288a7cb382 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -46,7 +46,7 @@ type mheap struct { nsmallfree [_NumSizeClasses]uint64 // number of frees for small objects (<=maxsmallsize) // range of addresses we might see in the heap - bitmap uintptr + bitmap uintptr // Points to one byte past the end of the bitmap bitmap_mapped uintptr arena_start uintptr arena_used uintptr // always mHeap_Map{Bits,Spans} before updating @@ -268,6 +268,28 @@ func inheap(b uintptr) bool { return true } +// inHeapOrStack is a variant of inheap that returns true for pointers into stack spans. +//go:nowritebarrier +//go:nosplit +func inHeapOrStack(b uintptr) bool { + if b == 0 || b < mheap_.arena_start || b >= mheap_.arena_used { + return false + } + // Not a beginning of a block, consult span table to find the block beginning. + s := h_spans[(b-mheap_.arena_start)>>_PageShift] + if s == nil || b < s.base() { + return false + } + switch s.state { + case mSpanInUse: + return b < s.limit + case _MSpanStack: + return b < s.base()+s.npages<<_PageShift + default: + return false + } +} + // TODO: spanOf and spanOfUnchecked are open-coded in a lot of places. // Use the functions instead. @@ -1221,7 +1243,7 @@ func freespecial(s *special, p unsafe.Pointer, size uintptr) { } } -const gcBitsChunkBytes = uintptr(1 << 16) +const gcBitsChunkBytes = uintptr(64 << 10) const gcBitsHeaderBytes = unsafe.Sizeof(gcBitsHeader{}) type gcBitsHeader struct { diff --git a/src/runtime/os1_freebsd.go b/src/runtime/os1_freebsd.go deleted file mode 100644 index 0dafe02325b792..00000000000000 --- a/src/runtime/os1_freebsd.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package runtime - -import ( - "runtime/internal/sys" - "unsafe" -) - -// From FreeBSD's -const ( - _CTL_HW = 6 - _HW_NCPU = 3 -) - -var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}} - -func getncpu() int32 { - mib := [2]uint32{_CTL_HW, _HW_NCPU} - out := uint32(0) - nout := unsafe.Sizeof(out) - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return int32(out) - } - return 1 -} - -// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and -// thus the code is largely similar. See Linux implementation -// and lock_futex.go for comments. - -//go:nosplit -func futexsleep(addr *uint32, val uint32, ns int64) { - systemstack(func() { - futexsleep1(addr, val, ns) - }) -} - -func futexsleep1(addr *uint32, val uint32, ns int64) { - var tsp *timespec - if ns >= 0 { - var ts timespec - ts.tv_nsec = 0 - ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) - tsp = &ts - } - ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, nil, tsp) - if ret >= 0 || ret == -_EINTR { - return - } - print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n") - *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005 -} - -//go:nosplit -func futexwakeup(addr *uint32, cnt uint32) { - ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, nil, nil) - if ret >= 0 { - return - } - - systemstack(func() { - print("umtx_wake_addr=", addr, " ret=", ret, "\n") - }) -} - -func thr_start() - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " thr_start=", funcPC(thr_start), " id=", mp.id, " ostk=", &mp, "\n") - } - - // NOTE(rsc): This code is confused. stackbase is the top of the stack - // and is equal to stk. However, it's working, so I'm not changing it. - param := thrparam{ - start_func: funcPC(thr_start), - arg: unsafe.Pointer(mp), - stack_base: mp.g0.stack.hi, - stack_size: uintptr(stk) - mp.g0.stack.hi, - child_tid: unsafe.Pointer(&mp.procid), - parent_tid: nil, - tls_base: unsafe.Pointer(&mp.tls[0]), - tls_size: unsafe.Sizeof(mp.tls), - } - - var oset sigset - sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - thr_new(¶m, int32(unsafe.Sizeof(param))) - sigprocmask(_SIG_SETMASK, &oset, nil) -} - -func osinit() { - ncpu = getncpu() -} - -var urandom_dev = []byte("/dev/urandom\x00") - -//go:nosplit -func getRandomData(r []byte) { - fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) - n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) - closefd(fd) - extendRandom(r, int(n)) -} - -func goenvs() { - goenvs_unix() -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) - mp.gsignal.m = mp -} - -//go:nosplit -func msigsave(mp *m) { - sigprocmask(_SIG_SETMASK, nil, &mp.sigmask) -} - -//go:nosplit -func msigrestore(sigmask sigset) { - sigprocmask(_SIG_SETMASK, &sigmask, nil) -} - -//go:nosplit -func sigblock() { - sigprocmask(_SIG_SETMASK, &sigset_all, nil) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - _g_ := getg() - - // m.procid is a uint64, but thr_new writes a uint32 on 32-bit systems. - // Fix it up. (Only matters on big-endian, but be clean anyway.) - if sys.PtrSize == 4 { - _g_.m.procid = uint64(*(*uint32)(unsafe.Pointer(&_g_.m.procid))) - } - - // Initialize signal handling. - var st stackt - sigaltstack(nil, &st) - if st.ss_flags&_SS_DISABLE != 0 { - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true - } else { - // Use existing signal stack. - stsp := uintptr(unsafe.Pointer(st.ss_sp)) - _g_.m.gsignal.stack.lo = stsp - _g_.m.gsignal.stack.hi = stsp + st.ss_size - _g_.m.gsignal.stackguard0 = stsp + _StackGuard - _g_.m.gsignal.stackguard1 = stsp + _StackGuard - _g_.m.gsignal.stackAlloc = st.ss_size - _g_.m.newSigstack = false - } - - // restore signal mask from m.sigmask and unblock essential signals - nmask := _g_.m.sigmask - for i := range sigtable { - if sigtable[i].flags&_SigUnblock != 0 { - nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) - } - } - sigprocmask(_SIG_SETMASK, &nmask, nil) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - if getg().m.newSigstack { - signalstack(nil) - } -} - -func memlimit() uintptr { - /* - TODO: Convert to Go when something actually uses the result. - Rlimit rl; - extern byte runtime·text[], runtime·end[]; - uintptr used; - - if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) - return 0; - if(rl.rlim_cur >= 0x7fffffff) - return 0; - - // Estimate our VM footprint excluding the heap. - // Not an exact science: use size of binary plus - // some room for thread stacks. - used = runtime·end - runtime·text + (64<<20); - if(used >= rl.rlim_cur) - return 0; - - // If there's not at least 16 MB left, we're probably - // not going to be able to do much. Treat as no limit. - rl.rlim_cur -= used; - if(rl.rlim_cur < (16<<20)) - return 0; - - return rl.rlim_cur - used; - */ - - return 0 -} - -func sigtramp() - -type sigactiont struct { - sa_handler uintptr - sa_flags int32 - sa_mask sigset -} - -//go:nosplit -//go:nowritebarrierrec -func setsig(i int32, fn uintptr, restart bool) { - var sa sigactiont - sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK - if restart { - sa.sa_flags |= _SA_RESTART - } - sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) - } - sa.sa_handler = fn - sigaction(i, &sa, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func setsigstack(i int32) { - throw("setsigstack") -} - -//go:nosplit -//go:nowritebarrierrec -func getsig(i int32) uintptr { - var sa sigactiont - sigaction(i, nil, &sa) - if sa.sa_handler == funcPC(sigtramp) { - return funcPC(sighandler) - } - return sa.sa_handler -} - -//go:nosplit -func signalstack(s *stack) { - var st stackt - if s == nil { - st.ss_flags = _SS_DISABLE - } else { - st.ss_sp = s.lo - st.ss_size = s.hi - s.lo - st.ss_flags = 0 - } - sigaltstack(&st, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func updatesigmask(m [(_NSIG + 31) / 32]uint32) { - var mask sigset - copy(mask.__bits[:], m[:]) - sigprocmask(_SIG_SETMASK, &mask, nil) -} - -func unblocksig(sig int32) { - var mask sigset - mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31) - sigprocmask(_SIG_UNBLOCK, &mask, nil) -} diff --git a/src/runtime/os1_nacl.go b/src/runtime/os1_nacl.go deleted file mode 100644 index feea4966529496..00000000000000 --- a/src/runtime/os1_nacl.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package runtime - -import "unsafe" - -type sigset struct{} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) - mp.gsignal.m = mp -} - -func sigtramp() - -//go:nosplit -func msigsave(mp *m) { -} - -//go:nosplit -func msigrestore(sigmask sigset) { -} - -//go:nosplit -func sigblock() { -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - _g_ := getg() - - // Initialize signal handling - ret := nacl_exception_stack(_g_.m.gsignal.stack.lo, 32*1024) - if ret < 0 { - print("runtime: nacl_exception_stack: error ", -ret, "\n") - } - - ret = nacl_exception_handler(funcPC(sigtramp), nil) - if ret < 0 { - print("runtime: nacl_exception_handler: error ", -ret, "\n") - } -} - -// Called from dropm to undo the effect of an minit. -func unminit() { -} - -func osinit() { - ncpu = 1 - getg().m.procid = 2 - //nacl_exception_handler(funcPC(sigtramp), nil); -} - -func signame(sig uint32) string { - if sig >= uint32(len(sigtable)) { - return "" - } - return sigtable[sig].name -} - -func crash() { - *(*int32)(nil) = 0 -} - -//go:noescape -func getRandomData([]byte) - -func goenvs() { - goenvs_unix() -} - -func initsig(preinit bool) { -} - -//go:nosplit -func usleep(us uint32) { - var ts timespec - - ts.tv_sec = int64(us / 1e6) - ts.tv_nsec = int32(us%1e6) * 1e3 - nacl_nanosleep(&ts, nil) -} - -func mstart_nacl() - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - mp.tls[0] = uintptr(unsafe.Pointer(mp.g0)) - mp.tls[1] = uintptr(unsafe.Pointer(mp)) - ret := nacl_thread_create(funcPC(mstart_nacl), stk, unsafe.Pointer(&mp.tls[2]), nil) - if ret < 0 { - print("nacl_thread_create: error ", -ret, "\n") - throw("newosproc") - } -} - -//go:nosplit -func semacreate(mp *m) { - if mp.waitsema != 0 { - return - } - systemstack(func() { - mu := nacl_mutex_create(0) - if mu < 0 { - print("nacl_mutex_create: error ", -mu, "\n") - throw("semacreate") - } - c := nacl_cond_create(0) - if c < 0 { - print("nacl_cond_create: error ", -c, "\n") - throw("semacreate") - } - mp.waitsema = c - mp.waitsemalock = mu - }) -} - -//go:nosplit -func semasleep(ns int64) int32 { - var ret int32 - - systemstack(func() { - _g_ := getg() - if nacl_mutex_lock(_g_.m.waitsemalock) < 0 { - throw("semasleep") - } - - for _g_.m.waitsemacount == 0 { - if ns < 0 { - if nacl_cond_wait(_g_.m.waitsema, _g_.m.waitsemalock) < 0 { - throw("semasleep") - } - } else { - var ts timespec - end := ns + nanotime() - ts.tv_sec = end / 1e9 - ts.tv_nsec = int32(end % 1e9) - r := nacl_cond_timed_wait_abs(_g_.m.waitsema, _g_.m.waitsemalock, &ts) - if r == -_ETIMEDOUT { - nacl_mutex_unlock(_g_.m.waitsemalock) - ret = -1 - return - } - if r < 0 { - throw("semasleep") - } - } - } - - _g_.m.waitsemacount = 0 - nacl_mutex_unlock(_g_.m.waitsemalock) - ret = 0 - }) - return ret -} - -//go:nosplit -func semawakeup(mp *m) { - systemstack(func() { - if nacl_mutex_lock(mp.waitsemalock) < 0 { - throw("semawakeup") - } - if mp.waitsemacount != 0 { - throw("semawakeup") - } - mp.waitsemacount = 1 - nacl_cond_signal(mp.waitsema) - nacl_mutex_unlock(mp.waitsemalock) - }) -} - -func memlimit() uintptr { - return 0 -} - -// This runs on a foreign stack, without an m or a g. No stack split. -//go:nosplit -//go:norace -//go:nowritebarrierrec -func badsignal(sig uintptr) { - cgocallback(unsafe.Pointer(funcPC(badsignalgo)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig)) -} - -func badsignalgo(sig uintptr) { - if !sigsend(uint32(sig)) { - // A foreign thread received the signal sig, and the - // Go code does not want to handle it. - raisebadsignal(int32(sig)) - } -} - -// This runs on a foreign stack, without an m or a g. No stack split. -//go:nosplit -func badsignal2() { - write(2, unsafe.Pointer(&badsignal1[0]), int32(len(badsignal1))) - exit(2) -} - -var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n") - -func raisebadsignal(sig int32) { - badsignal2() -} - -func madvise(addr unsafe.Pointer, n uintptr, flags int32) {} -func munmap(addr unsafe.Pointer, n uintptr) {} -func resetcpuprofiler(hz int32) {} -func sigdisable(uint32) {} -func sigenable(uint32) {} -func sigignore(uint32) {} -func closeonexec(int32) {} - -var writelock uint32 // test-and-set spin lock for write - -/* -An attempt at IRT. Doesn't work. See end of sys_nacl_amd64.s. - -void (*nacl_irt_query)(void); - -int8 nacl_irt_basic_v0_1_str[] = "nacl-irt-basic-0.1"; -void *nacl_irt_basic_v0_1[6]; // exit, gettod, clock, nanosleep, sched_yield, sysconf -int32 nacl_irt_basic_v0_1_size = sizeof(nacl_irt_basic_v0_1); - -int8 nacl_irt_memory_v0_3_str[] = "nacl-irt-memory-0.3"; -void *nacl_irt_memory_v0_3[3]; // mmap, munmap, mprotect -int32 nacl_irt_memory_v0_3_size = sizeof(nacl_irt_memory_v0_3); - -int8 nacl_irt_thread_v0_1_str[] = "nacl-irt-thread-0.1"; -void *nacl_irt_thread_v0_1[3]; // thread_create, thread_exit, thread_nice -int32 nacl_irt_thread_v0_1_size = sizeof(nacl_irt_thread_v0_1); -*/ diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go deleted file mode 100644 index 447dff81938e4a..00000000000000 --- a/src/runtime/os1_openbsd.go +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package runtime - -import ( - "runtime/internal/atomic" - "unsafe" -) - -const ( - _ESRCH = 3 - _EAGAIN = 35 - _EWOULDBLOCK = _EAGAIN - _ENOTSUP = 91 - - // From OpenBSD's sys/time.h - _CLOCK_REALTIME = 0 - _CLOCK_VIRTUAL = 1 - _CLOCK_PROF = 2 - _CLOCK_MONOTONIC = 3 -) - -type sigset uint32 - -const ( - sigset_none = sigset(0) - sigset_all = ^sigset(0) -) - -// From OpenBSD's -const ( - _CTL_HW = 6 - _HW_NCPU = 3 -) - -func getncpu() int32 { - mib := [2]uint32{_CTL_HW, _HW_NCPU} - out := uint32(0) - nout := unsafe.Sizeof(out) - - // Fetch hw.ncpu via sysctl. - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return int32(out) - } - return 1 -} - -//go:nosplit -func semacreate(mp *m) { -} - -//go:nosplit -func semasleep(ns int64) int32 { - _g_ := getg() - - // Compute sleep deadline. - var tsp *timespec - if ns >= 0 { - var ts timespec - var nsec int32 - ns += nanotime() - ts.set_sec(int64(timediv(ns, 1000000000, &nsec))) - ts.set_nsec(nsec) - tsp = &ts - } - - for { - v := atomic.Load(&_g_.m.waitsemacount) - if v > 0 { - if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { - return 0 // semaphore acquired - } - continue - } - - // Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0. - // - // From OpenBSD's __thrsleep(2) manual: - // "The abort argument, if not NULL, points to an int that will - // be examined [...] immediately before blocking. If that int - // is non-zero then __thrsleep() will immediately return EINTR - // without blocking." - ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.waitsemacount) - if ret == _EWOULDBLOCK { - return -1 - } - } -} - -//go:nosplit -func semawakeup(mp *m) { - atomic.Xadd(&mp.waitsemacount, 1) - ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1) - if ret != 0 && ret != _ESRCH { - // semawakeup can be called on signal stack. - systemstack(func() { - print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n") - }) - } -} - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n") - } - - param := tforkt{ - tf_tcb: unsafe.Pointer(&mp.tls[0]), - tf_tid: (*int32)(unsafe.Pointer(&mp.procid)), - tf_stack: uintptr(stk), - } - - oset := sigprocmask(_SIG_SETMASK, sigset_all) - ret := tfork(¶m, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart)) - sigprocmask(_SIG_SETMASK, oset) - - if ret < 0 { - print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n") - throw("runtime.newosproc") - } -} - -func osinit() { - ncpu = getncpu() -} - -var urandom_dev = []byte("/dev/urandom\x00") - -//go:nosplit -func getRandomData(r []byte) { - fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) - n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) - closefd(fd) - extendRandom(r, int(n)) -} - -func goenvs() { - goenvs_unix() -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) - mp.gsignal.m = mp -} - -//go:nosplit -func msigsave(mp *m) { - mp.sigmask = sigprocmask(_SIG_BLOCK, 0) -} - -//go:nosplit -func msigrestore(sigmask sigset) { - sigprocmask(_SIG_SETMASK, sigmask) -} - -//go:nosplit -func sigblock() { - sigprocmask(_SIG_SETMASK, sigset_all) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, can not allocate memory. -func minit() { - _g_ := getg() - - // m.procid is a uint64, but tfork writes an int32. Fix it up. - _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid))) - - // Initialize signal handling - var st stackt - sigaltstack(nil, &st) - if st.ss_flags&_SS_DISABLE != 0 { - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true - } else { - // Use existing signal stack. - stsp := uintptr(unsafe.Pointer(st.ss_sp)) - _g_.m.gsignal.stack.lo = stsp - _g_.m.gsignal.stack.hi = stsp + st.ss_size - _g_.m.gsignal.stackguard0 = stsp + _StackGuard - _g_.m.gsignal.stackguard1 = stsp + _StackGuard - _g_.m.gsignal.stackAlloc = st.ss_size - _g_.m.newSigstack = false - } - - // restore signal mask from m.sigmask and unblock essential signals - nmask := _g_.m.sigmask - for i := range sigtable { - if sigtable[i].flags&_SigUnblock != 0 { - nmask &^= 1 << (uint32(i) - 1) - } - } - sigprocmask(_SIG_SETMASK, nmask) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - if getg().m.newSigstack { - signalstack(nil) - } -} - -func memlimit() uintptr { - return 0 -} - -func sigtramp() - -type sigactiont struct { - sa_sigaction uintptr - sa_mask uint32 - sa_flags int32 -} - -//go:nosplit -//go:nowritebarrierrec -func setsig(i int32, fn uintptr, restart bool) { - var sa sigactiont - sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK - if restart { - sa.sa_flags |= _SA_RESTART - } - sa.sa_mask = uint32(sigset_all) - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) - } - sa.sa_sigaction = fn - sigaction(i, &sa, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func setsigstack(i int32) { - throw("setsigstack") -} - -//go:nosplit -//go:nowritebarrierrec -func getsig(i int32) uintptr { - var sa sigactiont - sigaction(i, nil, &sa) - if sa.sa_sigaction == funcPC(sigtramp) { - return funcPC(sighandler) - } - return sa.sa_sigaction -} - -//go:nosplit -func signalstack(s *stack) { - var st stackt - if s == nil { - st.ss_flags = _SS_DISABLE - } else { - st.ss_sp = s.lo - st.ss_size = s.hi - s.lo - st.ss_flags = 0 - } - sigaltstack(&st, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func updatesigmask(m sigmask) { - sigprocmask(_SIG_SETMASK, sigset(m[0])) -} - -func unblocksig(sig int32) { - mask := sigset(1) << (uint32(sig) - 1) - sigprocmask(_SIG_UNBLOCK, mask) -} diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go deleted file mode 100644 index 6c7e36d0620a0f..00000000000000 --- a/src/runtime/os1_plan9.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package runtime - -import ( - "runtime/internal/atomic" - "unsafe" -) - -type sigset struct{} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - // Initialize stack and goroutine for note handling. - mp.gsignal = malg(32 * 1024) - mp.gsignal.m = mp - mp.notesig = (*int8)(mallocgc(_ERRMAX, nil, true)) - // Initialize stack for handling strings from the - // errstr system call, as used in package syscall. - mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, true)) -} - -func msigsave(mp *m) { -} - -func msigrestore(sigmask sigset) { -} - -func sigblock() { -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - if atomic.Load(&exiting) != 0 { - exits(&emptystatus[0]) - } - // Mask all SSE floating-point exceptions - // when running on the 64-bit kernel. - setfpmasks() -} - -// Called from dropm to undo the effect of an minit. -func unminit() { -} - -var sysstat = []byte("/dev/sysstat\x00") - -func getproccount() int32 { - var buf [2048]byte - fd := open(&sysstat[0], _OREAD, 0) - if fd < 0 { - return 1 - } - ncpu := int32(0) - for { - n := read(fd, unsafe.Pointer(&buf), int32(len(buf))) - if n <= 0 { - break - } - for i := int32(0); i < n; i++ { - if buf[i] == '\n' { - ncpu++ - } - } - } - closefd(fd) - if ncpu == 0 { - ncpu = 1 - } - return ncpu -} - -var pid = []byte("#c/pid\x00") - -func getpid() uint64 { - var b [20]byte - fd := open(&pid[0], 0, 0) - if fd >= 0 { - read(fd, unsafe.Pointer(&b), int32(len(b))) - closefd(fd) - } - c := b[:] - for c[0] == ' ' || c[0] == '\t' { - c = c[1:] - } - return uint64(_atoi(c)) -} - -func osinit() { - initBloc() - ncpu = getproccount() - getg().m.procid = getpid() - notify(unsafe.Pointer(funcPC(sigtramp))) -} - -func crash() { - notify(nil) - *(*int)(nil) = 0 -} - -//go:nosplit -func getRandomData(r []byte) { - extendRandom(r, 0) -} - -func goenvs() { -} - -func initsig(preinit bool) { -} - -//go:nosplit -func osyield() { - sleep(0) -} - -//go:nosplit -func usleep(µs uint32) { - ms := int32(µs / 1000) - if ms == 0 { - ms = 1 - } - sleep(ms) -} - -//go:nosplit -func nanotime() int64 { - var scratch int64 - ns := nsec(&scratch) - // TODO(aram): remove hack after I fix _nsec in the pc64 kernel. - if ns == 0 { - return scratch - } - return ns -} - -//go:nosplit -func itoa(buf []byte, val uint64) []byte { - i := len(buf) - 1 - for val >= 10 { - buf[i] = byte(val%10 + '0') - i-- - val /= 10 - } - buf[i] = byte(val + '0') - return buf[i:] -} - -var goexits = []byte("go: exit ") -var emptystatus = []byte("\x00") -var exiting uint32 - -func goexitsall(status *byte) { - var buf [_ERRMAX]byte - if !atomic.Cas(&exiting, 0, 1) { - return - } - getg().m.locks++ - n := copy(buf[:], goexits) - n = copy(buf[n:], gostringnocopy(status)) - pid := getpid() - for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink { - if mp.procid != 0 && mp.procid != pid { - postnote(mp.procid, buf[:]) - } - } - getg().m.locks-- -} - -var procdir = []byte("/proc/") -var notefile = []byte("/note\x00") - -func postnote(pid uint64, msg []byte) int { - var buf [128]byte - var tmp [32]byte - n := copy(buf[:], procdir) - n += copy(buf[n:], itoa(tmp[:], pid)) - copy(buf[n:], notefile) - fd := open(&buf[0], _OWRITE, 0) - if fd < 0 { - return -1 - } - len := findnull(&msg[0]) - if write(uintptr(fd), unsafe.Pointer(&msg[0]), int32(len)) != int64(len) { - closefd(fd) - return -1 - } - closefd(fd) - return 0 -} - -//go:nosplit -func exit(e int) { - var status []byte - if e == 0 { - status = emptystatus - } else { - // build error string - var tmp [32]byte - status = append(itoa(tmp[:len(tmp)-1], uint64(e)), 0) - } - goexitsall(&status[0]) - exits(&status[0]) -} - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - if false { - print("newosproc mp=", mp, " ostk=", &mp, "\n") - } - pid := rfork(_RFPROC | _RFMEM | _RFNOWAIT) - if pid < 0 { - throw("newosproc: rfork failed") - } - if pid == 0 { - tstart_plan9(mp) - } -} - -//go:nosplit -func semacreate(mp *m) { -} - -//go:nosplit -func semasleep(ns int64) int { - _g_ := getg() - if ns >= 0 { - ms := timediv(ns, 1000000, nil) - if ms == 0 { - ms = 1 - } - ret := plan9_tsemacquire(&_g_.m.waitsemacount, ms) - if ret == 1 { - return 0 // success - } - return -1 // timeout or interrupted - } - for plan9_semacquire(&_g_.m.waitsemacount, 1) < 0 { - // interrupted; try again (c.f. lock_sema.go) - } - return 0 // success -} - -//go:nosplit -func semawakeup(mp *m) { - plan9_semrelease(&mp.waitsemacount, 1) -} - -//go:nosplit -func read(fd int32, buf unsafe.Pointer, n int32) int32 { - return pread(fd, buf, n, -1) -} - -//go:nosplit -func write(fd uintptr, buf unsafe.Pointer, n int32) int64 { - return int64(pwrite(int32(fd), buf, n, -1)) -} - -func memlimit() uint64 { - return 0 -} - -var _badsignal = []byte("runtime: signal received on thread not created by Go.\n") - -// This runs on a foreign stack, without an m or a g. No stack split. -//go:nosplit -func badsignal2() { - pwrite(2, unsafe.Pointer(&_badsignal[0]), int32(len(_badsignal)), -1) - exits(&_badsignal[0]) -} - -func raisebadsignal(sig int32) { - badsignal2() -} - -func _atoi(b []byte) int { - n := 0 - for len(b) > 0 && '0' <= b[0] && b[0] <= '9' { - n = n*10 + int(b[0]) - '0' - b = b[1:] - } - return n -} - -func signame(sig uint32) string { - if sig >= uint32(len(sigtable)) { - return "" - } - return sigtable[sig].name -} diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index 44830650e12952..3a73b6627743e3 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -1,10 +1,13 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime -import "unsafe" +import ( + "runtime/internal/sys" + "unsafe" +) type mOS struct{} @@ -35,3 +38,274 @@ func raiseproc(sig int32) func sys_umtx_op(addr *uint32, mode int32, val uint32, ptr2, ts *timespec) int32 func osyield() + +// From FreeBSD's +const ( + _CTL_HW = 6 + _HW_NCPU = 3 +) + +var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}} + +func getncpu() int32 { + mib := [2]uint32{_CTL_HW, _HW_NCPU} + out := uint32(0) + nout := unsafe.Sizeof(out) + ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret >= 0 { + return int32(out) + } + return 1 +} + +// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and +// thus the code is largely similar. See Linux implementation +// and lock_futex.go for comments. + +//go:nosplit +func futexsleep(addr *uint32, val uint32, ns int64) { + systemstack(func() { + futexsleep1(addr, val, ns) + }) +} + +func futexsleep1(addr *uint32, val uint32, ns int64) { + var tsp *timespec + if ns >= 0 { + var ts timespec + ts.tv_nsec = 0 + ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) + tsp = &ts + } + ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, nil, tsp) + if ret >= 0 || ret == -_EINTR { + return + } + print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n") + *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005 +} + +//go:nosplit +func futexwakeup(addr *uint32, cnt uint32) { + ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, nil, nil) + if ret >= 0 { + return + } + + systemstack(func() { + print("umtx_wake_addr=", addr, " ret=", ret, "\n") + }) +} + +func thr_start() + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + if false { + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " thr_start=", funcPC(thr_start), " id=", mp.id, " ostk=", &mp, "\n") + } + + // NOTE(rsc): This code is confused. stackbase is the top of the stack + // and is equal to stk. However, it's working, so I'm not changing it. + param := thrparam{ + start_func: funcPC(thr_start), + arg: unsafe.Pointer(mp), + stack_base: mp.g0.stack.hi, + stack_size: uintptr(stk) - mp.g0.stack.hi, + child_tid: unsafe.Pointer(&mp.procid), + parent_tid: nil, + tls_base: unsafe.Pointer(&mp.tls[0]), + tls_size: unsafe.Sizeof(mp.tls), + } + + var oset sigset + sigprocmask(_SIG_SETMASK, &sigset_all, &oset) + thr_new(¶m, int32(unsafe.Sizeof(param))) + sigprocmask(_SIG_SETMASK, &oset, nil) +} + +func osinit() { + ncpu = getncpu() +} + +var urandom_dev = []byte("/dev/urandom\x00") + +//go:nosplit +func getRandomData(r []byte) { + fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) + n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) + closefd(fd) + extendRandom(r, int(n)) +} + +func goenvs() { + goenvs_unix() +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { + sigprocmask(_SIG_SETMASK, nil, &mp.sigmask) +} + +//go:nosplit +func msigrestore(sigmask sigset) { + sigprocmask(_SIG_SETMASK, &sigmask, nil) +} + +//go:nosplit +func sigblock() { + sigprocmask(_SIG_SETMASK, &sigset_all, nil) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + _g_ := getg() + + // m.procid is a uint64, but thr_new writes a uint32 on 32-bit systems. + // Fix it up. (Only matters on big-endian, but be clean anyway.) + if sys.PtrSize == 4 { + _g_.m.procid = uint64(*(*uint32)(unsafe.Pointer(&_g_.m.procid))) + } + + // Initialize signal handling. + var st stackt + sigaltstack(nil, &st) + if st.ss_flags&_SS_DISABLE != 0 { + signalstack(&_g_.m.gsignal.stack) + _g_.m.newSigstack = true + } else { + // Use existing signal stack. + stsp := uintptr(unsafe.Pointer(st.ss_sp)) + _g_.m.gsignal.stack.lo = stsp + _g_.m.gsignal.stack.hi = stsp + st.ss_size + _g_.m.gsignal.stackguard0 = stsp + _StackGuard + _g_.m.gsignal.stackguard1 = stsp + _StackGuard + _g_.m.gsignal.stackAlloc = st.ss_size + _g_.m.newSigstack = false + } + + // restore signal mask from m.sigmask and unblock essential signals + nmask := _g_.m.sigmask + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) + } + } + sigprocmask(_SIG_SETMASK, &nmask, nil) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + if getg().m.newSigstack { + signalstack(nil) + } +} + +func memlimit() uintptr { + /* + TODO: Convert to Go when something actually uses the result. + Rlimit rl; + extern byte runtime·text[], runtime·end[]; + uintptr used; + + if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) + return 0; + if(rl.rlim_cur >= 0x7fffffff) + return 0; + + // Estimate our VM footprint excluding the heap. + // Not an exact science: use size of binary plus + // some room for thread stacks. + used = runtime·end - runtime·text + (64<<20); + if(used >= rl.rlim_cur) + return 0; + + // If there's not at least 16 MB left, we're probably + // not going to be able to do much. Treat as no limit. + rl.rlim_cur -= used; + if(rl.rlim_cur < (16<<20)) + return 0; + + return rl.rlim_cur - used; + */ + + return 0 +} + +func sigtramp() + +type sigactiont struct { + sa_handler uintptr + sa_flags int32 + sa_mask sigset +} + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa sigactiont + sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK + if restart { + sa.sa_flags |= _SA_RESTART + } + sa.sa_mask = sigset_all + if fn == funcPC(sighandler) { + fn = funcPC(sigtramp) + } + sa.sa_handler = fn + sigaction(i, &sa, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + throw("setsigstack") +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa sigactiont + sigaction(i, nil, &sa) + if sa.sa_handler == funcPC(sigtramp) { + return funcPC(sighandler) + } + return sa.sa_handler +} + +//go:nosplit +func signalstack(s *stack) { + var st stackt + if s == nil { + st.ss_flags = _SS_DISABLE + } else { + st.ss_sp = s.lo + st.ss_size = s.hi - s.lo + st.ss_flags = 0 + } + sigaltstack(&st, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m [(_NSIG + 31) / 32]uint32) { + var mask sigset + copy(mask.__bits[:], m[:]) + sigprocmask(_SIG_SETMASK, &mask, nil) +} + +func unblocksig(sig int32) { + var mask sigset + mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31) + sigprocmask(_SIG_UNBLOCK, &mask, nil) +} diff --git a/src/runtime/os_linux_mips64x.go b/src/runtime/os_linux_mips64x.go index 92b5c82af7c20f..8039b2fac9b7a3 100644 --- a/src/runtime/os_linux_mips64x.go +++ b/src/runtime/os_linux_mips64x.go @@ -9,6 +9,17 @@ package runtime var randomNumber uint32 +func archauxv(tag, val uintptr) { + switch tag { + case _AT_RANDOM: + // sysargs filled in startupRandomData, but that + // pointer may not be word aligned, so we must treat + // it as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + } +} + //go:nosplit func cputicks() int64 { // Currently cputicks() is used in blocking profiler and to seed fastrand1(). diff --git a/src/runtime/os_linux_noauxv.go b/src/runtime/os_linux_noauxv.go index 0b46f594ce0117..22522dd803dc02 100644 --- a/src/runtime/os_linux_noauxv.go +++ b/src/runtime/os_linux_noauxv.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !amd64,!arm,!arm64 +// +build !amd64,!arm,!arm64,!mips64,!mips64le package runtime diff --git a/src/runtime/os_nacl.go b/src/runtime/os_nacl.go index 6f126b4770505e..6cbd16de159c8c 100644 --- a/src/runtime/os_nacl.go +++ b/src/runtime/os_nacl.go @@ -1,4 +1,4 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -67,3 +67,233 @@ func raiseproc(sig int32) { func open(name *byte, mode, perm int32) int32 func closefd(fd int32) int32 func read(fd int32, p unsafe.Pointer, n int32) int32 + +type sigset struct{} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp +} + +func sigtramp() + +//go:nosplit +func msigsave(mp *m) { +} + +//go:nosplit +func msigrestore(sigmask sigset) { +} + +//go:nosplit +func sigblock() { +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + _g_ := getg() + + // Initialize signal handling + ret := nacl_exception_stack(_g_.m.gsignal.stack.lo, 32*1024) + if ret < 0 { + print("runtime: nacl_exception_stack: error ", -ret, "\n") + } + + ret = nacl_exception_handler(funcPC(sigtramp), nil) + if ret < 0 { + print("runtime: nacl_exception_handler: error ", -ret, "\n") + } +} + +// Called from dropm to undo the effect of an minit. +func unminit() { +} + +func osinit() { + ncpu = 1 + getg().m.procid = 2 + //nacl_exception_handler(funcPC(sigtramp), nil); +} + +func signame(sig uint32) string { + if sig >= uint32(len(sigtable)) { + return "" + } + return sigtable[sig].name +} + +func crash() { + *(*int32)(nil) = 0 +} + +//go:noescape +func getRandomData([]byte) + +func goenvs() { + goenvs_unix() +} + +func initsig(preinit bool) { +} + +//go:nosplit +func usleep(us uint32) { + var ts timespec + + ts.tv_sec = int64(us / 1e6) + ts.tv_nsec = int32(us%1e6) * 1e3 + nacl_nanosleep(&ts, nil) +} + +func mstart_nacl() + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + mp.tls[0] = uintptr(unsafe.Pointer(mp.g0)) + mp.tls[1] = uintptr(unsafe.Pointer(mp)) + ret := nacl_thread_create(funcPC(mstart_nacl), stk, unsafe.Pointer(&mp.tls[2]), nil) + if ret < 0 { + print("nacl_thread_create: error ", -ret, "\n") + throw("newosproc") + } +} + +//go:nosplit +func semacreate(mp *m) { + if mp.waitsema != 0 { + return + } + systemstack(func() { + mu := nacl_mutex_create(0) + if mu < 0 { + print("nacl_mutex_create: error ", -mu, "\n") + throw("semacreate") + } + c := nacl_cond_create(0) + if c < 0 { + print("nacl_cond_create: error ", -c, "\n") + throw("semacreate") + } + mp.waitsema = c + mp.waitsemalock = mu + }) +} + +//go:nosplit +func semasleep(ns int64) int32 { + var ret int32 + + systemstack(func() { + _g_ := getg() + if nacl_mutex_lock(_g_.m.waitsemalock) < 0 { + throw("semasleep") + } + + for _g_.m.waitsemacount == 0 { + if ns < 0 { + if nacl_cond_wait(_g_.m.waitsema, _g_.m.waitsemalock) < 0 { + throw("semasleep") + } + } else { + var ts timespec + end := ns + nanotime() + ts.tv_sec = end / 1e9 + ts.tv_nsec = int32(end % 1e9) + r := nacl_cond_timed_wait_abs(_g_.m.waitsema, _g_.m.waitsemalock, &ts) + if r == -_ETIMEDOUT { + nacl_mutex_unlock(_g_.m.waitsemalock) + ret = -1 + return + } + if r < 0 { + throw("semasleep") + } + } + } + + _g_.m.waitsemacount = 0 + nacl_mutex_unlock(_g_.m.waitsemalock) + ret = 0 + }) + return ret +} + +//go:nosplit +func semawakeup(mp *m) { + systemstack(func() { + if nacl_mutex_lock(mp.waitsemalock) < 0 { + throw("semawakeup") + } + if mp.waitsemacount != 0 { + throw("semawakeup") + } + mp.waitsemacount = 1 + nacl_cond_signal(mp.waitsema) + nacl_mutex_unlock(mp.waitsemalock) + }) +} + +func memlimit() uintptr { + return 0 +} + +// This runs on a foreign stack, without an m or a g. No stack split. +//go:nosplit +//go:norace +//go:nowritebarrierrec +func badsignal(sig uintptr) { + cgocallback(unsafe.Pointer(funcPC(badsignalgo)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig)) +} + +func badsignalgo(sig uintptr) { + if !sigsend(uint32(sig)) { + // A foreign thread received the signal sig, and the + // Go code does not want to handle it. + raisebadsignal(int32(sig)) + } +} + +// This runs on a foreign stack, without an m or a g. No stack split. +//go:nosplit +func badsignal2() { + write(2, unsafe.Pointer(&badsignal1[0]), int32(len(badsignal1))) + exit(2) +} + +var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n") + +func raisebadsignal(sig int32) { + badsignal2() +} + +func madvise(addr unsafe.Pointer, n uintptr, flags int32) {} +func munmap(addr unsafe.Pointer, n uintptr) {} +func resetcpuprofiler(hz int32) {} +func sigdisable(uint32) {} +func sigenable(uint32) {} +func sigignore(uint32) {} +func closeonexec(int32) {} + +var writelock uint32 // test-and-set spin lock for write + +/* +An attempt at IRT. Doesn't work. See end of sys_nacl_amd64.s. + +void (*nacl_irt_query)(void); + +int8 nacl_irt_basic_v0_1_str[] = "nacl-irt-basic-0.1"; +void *nacl_irt_basic_v0_1[6]; // exit, gettod, clock, nanosleep, sched_yield, sysconf +int32 nacl_irt_basic_v0_1_size = sizeof(nacl_irt_basic_v0_1); + +int8 nacl_irt_memory_v0_3_str[] = "nacl-irt-memory-0.3"; +void *nacl_irt_memory_v0_3[3]; // mmap, munmap, mprotect +int32 nacl_irt_memory_v0_3_size = sizeof(nacl_irt_memory_v0_3); + +int8 nacl_irt_thread_v0_1_str[] = "nacl-irt-thread-0.1"; +void *nacl_irt_thread_v0_1[3]; // thread_create, thread_exit, thread_nice +int32 nacl_irt_thread_v0_1_size = sizeof(nacl_irt_thread_v0_1); +*/ diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index 3748ed2e514cc1..ded6b1d4ea4a83 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -1,9 +1,14 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime +import ( + "runtime/internal/atomic" + "unsafe" +) + type mOS struct { waitsemacount uint32 } @@ -36,3 +41,271 @@ func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort func thrwakeup(ident uintptr, n int32) int32 func osyield() + +const ( + _ESRCH = 3 + _EAGAIN = 35 + _EWOULDBLOCK = _EAGAIN + _ENOTSUP = 91 + + // From OpenBSD's sys/time.h + _CLOCK_REALTIME = 0 + _CLOCK_VIRTUAL = 1 + _CLOCK_PROF = 2 + _CLOCK_MONOTONIC = 3 +) + +type sigset uint32 + +const ( + sigset_none = sigset(0) + sigset_all = ^sigset(0) +) + +// From OpenBSD's +const ( + _CTL_HW = 6 + _HW_NCPU = 3 +) + +func getncpu() int32 { + mib := [2]uint32{_CTL_HW, _HW_NCPU} + out := uint32(0) + nout := unsafe.Sizeof(out) + + // Fetch hw.ncpu via sysctl. + ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret >= 0 { + return int32(out) + } + return 1 +} + +//go:nosplit +func semacreate(mp *m) { +} + +//go:nosplit +func semasleep(ns int64) int32 { + _g_ := getg() + + // Compute sleep deadline. + var tsp *timespec + if ns >= 0 { + var ts timespec + var nsec int32 + ns += nanotime() + ts.set_sec(int64(timediv(ns, 1000000000, &nsec))) + ts.set_nsec(nsec) + tsp = &ts + } + + for { + v := atomic.Load(&_g_.m.waitsemacount) + if v > 0 { + if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { + return 0 // semaphore acquired + } + continue + } + + // Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0. + // + // From OpenBSD's __thrsleep(2) manual: + // "The abort argument, if not NULL, points to an int that will + // be examined [...] immediately before blocking. If that int + // is non-zero then __thrsleep() will immediately return EINTR + // without blocking." + ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.waitsemacount) + if ret == _EWOULDBLOCK { + return -1 + } + } +} + +//go:nosplit +func semawakeup(mp *m) { + atomic.Xadd(&mp.waitsemacount, 1) + ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1) + if ret != 0 && ret != _ESRCH { + // semawakeup can be called on signal stack. + systemstack(func() { + print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n") + }) + } +} + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + if false { + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n") + } + + param := tforkt{ + tf_tcb: unsafe.Pointer(&mp.tls[0]), + tf_tid: (*int32)(unsafe.Pointer(&mp.procid)), + tf_stack: uintptr(stk), + } + + oset := sigprocmask(_SIG_SETMASK, sigset_all) + ret := tfork(¶m, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart)) + sigprocmask(_SIG_SETMASK, oset) + + if ret < 0 { + print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n") + throw("runtime.newosproc") + } +} + +func osinit() { + ncpu = getncpu() +} + +var urandom_dev = []byte("/dev/urandom\x00") + +//go:nosplit +func getRandomData(r []byte) { + fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) + n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) + closefd(fd) + extendRandom(r, int(n)) +} + +func goenvs() { + goenvs_unix() +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { + mp.sigmask = sigprocmask(_SIG_BLOCK, 0) +} + +//go:nosplit +func msigrestore(sigmask sigset) { + sigprocmask(_SIG_SETMASK, sigmask) +} + +//go:nosplit +func sigblock() { + sigprocmask(_SIG_SETMASK, sigset_all) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. +func minit() { + _g_ := getg() + + // m.procid is a uint64, but tfork writes an int32. Fix it up. + _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid))) + + // Initialize signal handling + var st stackt + sigaltstack(nil, &st) + if st.ss_flags&_SS_DISABLE != 0 { + signalstack(&_g_.m.gsignal.stack) + _g_.m.newSigstack = true + } else { + // Use existing signal stack. + stsp := uintptr(unsafe.Pointer(st.ss_sp)) + _g_.m.gsignal.stack.lo = stsp + _g_.m.gsignal.stack.hi = stsp + st.ss_size + _g_.m.gsignal.stackguard0 = stsp + _StackGuard + _g_.m.gsignal.stackguard1 = stsp + _StackGuard + _g_.m.gsignal.stackAlloc = st.ss_size + _g_.m.newSigstack = false + } + + // restore signal mask from m.sigmask and unblock essential signals + nmask := _g_.m.sigmask + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + nmask &^= 1 << (uint32(i) - 1) + } + } + sigprocmask(_SIG_SETMASK, nmask) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + if getg().m.newSigstack { + signalstack(nil) + } +} + +func memlimit() uintptr { + return 0 +} + +func sigtramp() + +type sigactiont struct { + sa_sigaction uintptr + sa_mask uint32 + sa_flags int32 +} + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa sigactiont + sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK + if restart { + sa.sa_flags |= _SA_RESTART + } + sa.sa_mask = uint32(sigset_all) + if fn == funcPC(sighandler) { + fn = funcPC(sigtramp) + } + sa.sa_sigaction = fn + sigaction(i, &sa, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + throw("setsigstack") +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa sigactiont + sigaction(i, nil, &sa) + if sa.sa_sigaction == funcPC(sigtramp) { + return funcPC(sighandler) + } + return sa.sa_sigaction +} + +//go:nosplit +func signalstack(s *stack) { + var st stackt + if s == nil { + st.ss_flags = _SS_DISABLE + } else { + st.ss_sp = s.lo + st.ss_size = s.hi - s.lo + st.ss_flags = 0 + } + sigaltstack(&st, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m sigmask) { + sigprocmask(_SIG_SETMASK, sigset(m[0])) +} + +func unblocksig(sig int32) { + mask := sigset(1) << (uint32(sig) - 1) + sigprocmask(_SIG_UNBLOCK, mask) +} diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go index 5c43a3bd854b34..2f3a0d1a19b2fd 100644 --- a/src/runtime/os_plan9.go +++ b/src/runtime/os_plan9.go @@ -1,10 +1,13 @@ -// Copyright 2014 The Go Authors. All rights reserved. +// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime -import "unsafe" +import ( + "runtime/internal/atomic" + "unsafe" +) type mOS struct { waitsemacount uint32 @@ -148,3 +151,288 @@ func atolwhex(p string) int64 { } return n } + +type sigset struct{} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + // Initialize stack and goroutine for note handling. + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp + mp.notesig = (*int8)(mallocgc(_ERRMAX, nil, true)) + // Initialize stack for handling strings from the + // errstr system call, as used in package syscall. + mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, true)) +} + +func msigsave(mp *m) { +} + +func msigrestore(sigmask sigset) { +} + +func sigblock() { +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + if atomic.Load(&exiting) != 0 { + exits(&emptystatus[0]) + } + // Mask all SSE floating-point exceptions + // when running on the 64-bit kernel. + setfpmasks() +} + +// Called from dropm to undo the effect of an minit. +func unminit() { +} + +var sysstat = []byte("/dev/sysstat\x00") + +func getproccount() int32 { + var buf [2048]byte + fd := open(&sysstat[0], _OREAD, 0) + if fd < 0 { + return 1 + } + ncpu := int32(0) + for { + n := read(fd, unsafe.Pointer(&buf), int32(len(buf))) + if n <= 0 { + break + } + for i := int32(0); i < n; i++ { + if buf[i] == '\n' { + ncpu++ + } + } + } + closefd(fd) + if ncpu == 0 { + ncpu = 1 + } + return ncpu +} + +var pid = []byte("#c/pid\x00") + +func getpid() uint64 { + var b [20]byte + fd := open(&pid[0], 0, 0) + if fd >= 0 { + read(fd, unsafe.Pointer(&b), int32(len(b))) + closefd(fd) + } + c := b[:] + for c[0] == ' ' || c[0] == '\t' { + c = c[1:] + } + return uint64(_atoi(c)) +} + +func osinit() { + initBloc() + ncpu = getproccount() + getg().m.procid = getpid() + notify(unsafe.Pointer(funcPC(sigtramp))) +} + +func crash() { + notify(nil) + *(*int)(nil) = 0 +} + +//go:nosplit +func getRandomData(r []byte) { + extendRandom(r, 0) +} + +func goenvs() { +} + +func initsig(preinit bool) { +} + +//go:nosplit +func osyield() { + sleep(0) +} + +//go:nosplit +func usleep(µs uint32) { + ms := int32(µs / 1000) + if ms == 0 { + ms = 1 + } + sleep(ms) +} + +//go:nosplit +func nanotime() int64 { + var scratch int64 + ns := nsec(&scratch) + // TODO(aram): remove hack after I fix _nsec in the pc64 kernel. + if ns == 0 { + return scratch + } + return ns +} + +//go:nosplit +func itoa(buf []byte, val uint64) []byte { + i := len(buf) - 1 + for val >= 10 { + buf[i] = byte(val%10 + '0') + i-- + val /= 10 + } + buf[i] = byte(val + '0') + return buf[i:] +} + +var goexits = []byte("go: exit ") +var emptystatus = []byte("\x00") +var exiting uint32 + +func goexitsall(status *byte) { + var buf [_ERRMAX]byte + if !atomic.Cas(&exiting, 0, 1) { + return + } + getg().m.locks++ + n := copy(buf[:], goexits) + n = copy(buf[n:], gostringnocopy(status)) + pid := getpid() + for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink { + if mp.procid != 0 && mp.procid != pid { + postnote(mp.procid, buf[:]) + } + } + getg().m.locks-- +} + +var procdir = []byte("/proc/") +var notefile = []byte("/note\x00") + +func postnote(pid uint64, msg []byte) int { + var buf [128]byte + var tmp [32]byte + n := copy(buf[:], procdir) + n += copy(buf[n:], itoa(tmp[:], pid)) + copy(buf[n:], notefile) + fd := open(&buf[0], _OWRITE, 0) + if fd < 0 { + return -1 + } + len := findnull(&msg[0]) + if write(uintptr(fd), unsafe.Pointer(&msg[0]), int32(len)) != int64(len) { + closefd(fd) + return -1 + } + closefd(fd) + return 0 +} + +//go:nosplit +func exit(e int) { + var status []byte + if e == 0 { + status = emptystatus + } else { + // build error string + var tmp [32]byte + status = append(itoa(tmp[:len(tmp)-1], uint64(e)), 0) + } + goexitsall(&status[0]) + exits(&status[0]) +} + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + if false { + print("newosproc mp=", mp, " ostk=", &mp, "\n") + } + pid := rfork(_RFPROC | _RFMEM | _RFNOWAIT) + if pid < 0 { + throw("newosproc: rfork failed") + } + if pid == 0 { + tstart_plan9(mp) + } +} + +//go:nosplit +func semacreate(mp *m) { +} + +//go:nosplit +func semasleep(ns int64) int { + _g_ := getg() + if ns >= 0 { + ms := timediv(ns, 1000000, nil) + if ms == 0 { + ms = 1 + } + ret := plan9_tsemacquire(&_g_.m.waitsemacount, ms) + if ret == 1 { + return 0 // success + } + return -1 // timeout or interrupted + } + for plan9_semacquire(&_g_.m.waitsemacount, 1) < 0 { + // interrupted; try again (c.f. lock_sema.go) + } + return 0 // success +} + +//go:nosplit +func semawakeup(mp *m) { + plan9_semrelease(&mp.waitsemacount, 1) +} + +//go:nosplit +func read(fd int32, buf unsafe.Pointer, n int32) int32 { + return pread(fd, buf, n, -1) +} + +//go:nosplit +func write(fd uintptr, buf unsafe.Pointer, n int32) int64 { + return int64(pwrite(int32(fd), buf, n, -1)) +} + +func memlimit() uint64 { + return 0 +} + +var _badsignal = []byte("runtime: signal received on thread not created by Go.\n") + +// This runs on a foreign stack, without an m or a g. No stack split. +//go:nosplit +func badsignal2() { + pwrite(2, unsafe.Pointer(&_badsignal[0]), int32(len(_badsignal)), -1) + exits(&_badsignal[0]) +} + +func raisebadsignal(sig int32) { + badsignal2() +} + +func _atoi(b []byte) int { + n := 0 + for len(b) > 0 && '0' <= b[0] && b[0] <= '9' { + n = n*10 + int(b[0]) - '0' + b = b[1:] + } + return n +} + +func signame(sig uint32) string { + if sig >= uint32(len(sigtable)) { + return "" + } + return sigtable[sig].name +} diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 8b2f3d5291ee12..3852d93e7264c6 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -388,7 +388,7 @@ func TestStackBarrierProfiling(t *testing.T) { args = append(args, "-test.short") } cmd := exec.Command(os.Args[0], args...) - cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1"}, os.Environ()...) + cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1", "GOTRACEBACK=system"}, os.Environ()...) if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("subprocess failed with %v:\n%s", err, out) } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index d7e51d7deb872a..727c991a577c1c 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -155,12 +155,6 @@ func main() { if _cgo_thread_start == nil { throw("_cgo_thread_start missing") } - if _cgo_malloc == nil { - throw("_cgo_malloc missing") - } - if _cgo_free == nil { - throw("_cgo_free missing") - } if GOOS != "windows" { if _cgo_setenv == nil { throw("_cgo_setenv missing") @@ -273,7 +267,7 @@ func goparkunlock(lock *mutex, reason string, traceEv byte, traceskip int) { func goready(gp *g, traceskip int) { systemstack(func() { - ready(gp, traceskip) + ready(gp, traceskip, true) }) } @@ -440,9 +434,6 @@ func schedinit() { sched.maxmcount = 10000 - // Cache the framepointer experiment. This affects stack unwinding. - framepointer_enabled = haveexperiment("framepointer") - tracebackinit() moduledataverify() stackinit() @@ -533,7 +524,7 @@ func mcommoninit(mp *m) { } // Mark gp ready to run. -func ready(gp *g, traceskip int) { +func ready(gp *g, traceskip int, next bool) { if trace.enabled { traceGoUnpark(gp, traceskip) } @@ -550,7 +541,7 @@ func ready(gp *g, traceskip int) { // status is Gwaiting or Gscanwaiting, make Grunnable and put on runq casgstatus(gp, _Gwaiting, _Grunnable) - runqput(_g_.m.p.ptr(), gp, true) + runqput(_g_.m.p.ptr(), gp, next) if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 { // TODO: fast atomic wakep() } @@ -792,7 +783,7 @@ func casgcopystack(gp *g) uint32 { // scang blocks until gp's stack has been scanned. // It might be scanned by scang or it might be scanned by the goroutine itself. // Either way, the stack scan has completed when scang returns. -func scang(gp *g) { +func scang(gp *g, gcw *gcWork) { // Invariant; we (the caller, markroot for a specific goroutine) own gp.gcscandone. // Nothing is racing with us now, but gcscandone might be set to true left over // from an earlier round of stack scanning (we scan twice per GC). @@ -833,7 +824,7 @@ loop: // the goroutine until we're done. if castogscanstatus(gp, s, s|_Gscan) { if !gp.gcscandone { - scanstack(gp) + scanstack(gp, gcw) gp.gcscandone = true } restartg(gp) @@ -1835,7 +1826,7 @@ top: } if fingwait && fingwake { if gp := wakefing(); gp != nil { - ready(gp, 0) + ready(gp, 0, true) } } @@ -2445,7 +2436,12 @@ func exitsyscall(dummy int32) { _g_.m.locks++ // see comment in entersyscall if getcallersp(unsafe.Pointer(&dummy)) > _g_.syscallsp { - throw("exitsyscall: syscall frame is no longer valid") + // throw calls print which may try to grow the stack, + // but throwsplit == true so the stack can not be grown; + // use systemstack to avoid that possible problem. + systemstack(func() { + throw("exitsyscall: syscall frame is no longer valid") + }) } _g_.waitsince = 0 @@ -4164,6 +4160,9 @@ func setMaxThreads(in int) (out int) { } func haveexperiment(name string) bool { + if name == "framepointer" { + return framepointer_enabled // set by linker + } x := sys.Goexperiment for x != "" { xname := "" @@ -4176,6 +4175,9 @@ func haveexperiment(name string) bool { if xname == name { return true } + if len(xname) > 2 && xname[:2] == "no" && xname[2:] == name { + return false + } } return false } diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 89941210719954..22e4dca771c6c0 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -344,6 +344,14 @@ func TestGCFairness(t *testing.T) { } } +func TestGCFairness2(t *testing.T) { + output := runTestProg(t, "testprog", "GCFairness2") + want := "OK\n" + if output != want { + t.Fatalf("want %s, got %s\n", want, output) + } +} + func TestNumGoroutine(t *testing.T) { output := runTestProg(t, "testprog", "NumGoroutine") want := "1\n" diff --git a/src/runtime/race.go b/src/runtime/race.go index ecd68d80ce5249..42da936ddbf695 100644 --- a/src/runtime/race.go +++ b/src/runtime/race.go @@ -283,8 +283,16 @@ func raceinit() (gctx, pctx uintptr) { return } +var raceFiniLock mutex + //go:nosplit func racefini() { + // racefini() can only be called once to avoid races. + // This eventually (via __tsan_fini) calls C.exit which has + // undefined behavior if called more than once. If the lock is + // already held it's assumed that the first caller exits the program + // so other calls can hang forever without an issue. + lock(&raceFiniLock) racecall(&__tsan_fini, 0, 0, 0, 0) } diff --git a/src/runtime/race/testdata/chan_test.go b/src/runtime/race/testdata/chan_test.go index cddd9a6e782f2a..449191639e6180 100644 --- a/src/runtime/race/testdata/chan_test.go +++ b/src/runtime/race/testdata/chan_test.go @@ -285,17 +285,20 @@ func TestRaceChanWrongClose(t *testing.T) { v1 := 0 v2 := 0 c := make(chan int, 1) + done := make(chan bool) go func() { defer func() { recover() }() v1 = 1 c <- 1 + done <- true }() go func() { time.Sleep(1e7) v2 = 2 close(c) + done <- true }() time.Sleep(2e7) if _, who := <-c; who { @@ -303,6 +306,8 @@ func TestRaceChanWrongClose(t *testing.T) { } else { v1 = 2 } + <-done + <-done } func TestRaceChanSendClose(t *testing.T) { diff --git a/src/runtime/rt0_darwin_amd64.s b/src/runtime/rt0_darwin_amd64.s index ad46fd406dc208..655e77a86bde72 100644 --- a/src/runtime/rt0_darwin_amd64.s +++ b/src/runtime/rt0_darwin_amd64.s @@ -12,7 +12,14 @@ TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8 // When linking with -shared, this symbol is called when the shared library // is loaded. -TEXT _rt0_amd64_darwin_lib(SB),NOSPLIT,$0x48 +TEXT _rt0_amd64_darwin_lib(SB),NOSPLIT,$0x58 + // Align stack. We don't know whether Go is adding a frame pointer here or not. + MOVQ SP, R8 + SUBQ $16, R8 + ANDQ $~15, R8 + XCHGQ SP, R8 + + MOVQ R8, 0x48(SP) MOVQ BX, 0x18(SP) MOVQ BP, 0x20(SP) MOVQ R12, 0x28(SP) @@ -51,6 +58,9 @@ restore: MOVQ 0x30(SP), R13 MOVQ 0x38(SP), R14 MOVQ 0x40(SP), R15 + + MOVQ 0x48(SP), R8 + MOVQ R8, SP RET TEXT _rt0_amd64_darwin_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_darwin_arm.s b/src/runtime/rt0_darwin_arm.s index 59733d3ff60fe0..526d88f13d2430 100644 --- a/src/runtime/rt0_darwin_arm.s +++ b/src/runtime/rt0_darwin_arm.s @@ -16,7 +16,7 @@ TEXT _rt0_arm_darwin(SB),7,$-4 // // Note that all currently shipping darwin/arm platforms require // cgo and do not support c-shared. -TEXT _rt0_arm_darwin_lib(SB),NOSPLIT,$32 +TEXT _rt0_arm_darwin_lib(SB),NOSPLIT,$104 // Preserve callee-save registers. MOVW R4, 12(R13) MOVW R5, 16(R13) @@ -25,6 +25,15 @@ TEXT _rt0_arm_darwin_lib(SB),NOSPLIT,$32 MOVW R8, 28(R13) MOVW R11, 32(R13) + MOVD F8, (32+8*1)(R13) + MOVD F9, (32+8*2)(R13) + MOVD F10, (32+8*3)(R13) + MOVD F11, (32+8*4)(R13) + MOVD F12, (32+8*5)(R13) + MOVD F13, (32+8*6)(R13) + MOVD F14, (32+8*7)(R13) + MOVD F15, (32+8*8)(R13) + MOVW R0, _rt0_arm_darwin_lib_argc<>(SB) MOVW R1, _rt0_arm_darwin_lib_argv<>(SB) @@ -57,6 +66,14 @@ rr: MOVW 24(R13), R7 MOVW 28(R13), R8 MOVW 32(R13), R11 + MOVD (32+8*1)(R13), F8 + MOVD (32+8*2)(R13), F9 + MOVD (32+8*3)(R13), F10 + MOVD (32+8*4)(R13), F11 + MOVD (32+8*5)(R13), F12 + MOVD (32+8*6)(R13), F13 + MOVD (32+8*7)(R13), F14 + MOVD (32+8*8)(R13), F15 RET diff --git a/src/runtime/rt0_linux_arm.s b/src/runtime/rt0_linux_arm.s index a4419b898e31ef..597e642adb7177 100644 --- a/src/runtime/rt0_linux_arm.s +++ b/src/runtime/rt0_linux_arm.s @@ -12,7 +12,7 @@ TEXT _rt0_arm_linux(SB),NOSPLIT,$-4 // When building with -buildmode=c-shared, this symbol is called when the shared // library is loaded. -TEXT _rt0_arm_linux_lib(SB),NOSPLIT,$32 +TEXT _rt0_arm_linux_lib(SB),NOSPLIT,$104 // Preserve callee-save registers. Raspberry Pi's dlopen(), for example, // actually cares that R11 is preserved. MOVW R4, 12(R13) @@ -22,6 +22,19 @@ TEXT _rt0_arm_linux_lib(SB),NOSPLIT,$32 MOVW R8, 28(R13) MOVW R11, 32(R13) + // Skip floating point registers on GOARM < 6. + MOVB runtime·goarm(SB), R11 + CMP $6, R11 + BLT skipfpsave + MOVD F8, (32+8*1)(R13) + MOVD F9, (32+8*2)(R13) + MOVD F10, (32+8*3)(R13) + MOVD F11, (32+8*4)(R13) + MOVD F12, (32+8*5)(R13) + MOVD F13, (32+8*6)(R13) + MOVD F14, (32+8*7)(R13) + MOVD F15, (32+8*8)(R13) +skipfpsave: // Save argc/argv. MOVW R0, _rt0_arm_linux_lib_argc<>(SB) MOVW R1, _rt0_arm_linux_lib_argv<>(SB) @@ -46,6 +59,18 @@ nocgo: BL runtime·newosproc0(SB) rr: // Restore callee-save registers and return. + MOVB runtime·goarm(SB), R11 + CMP $6, R11 + BLT skipfprest + MOVD (32+8*1)(R13), F8 + MOVD (32+8*2)(R13), F9 + MOVD (32+8*3)(R13), F10 + MOVD (32+8*4)(R13), F11 + MOVD (32+8*5)(R13), F12 + MOVD (32+8*6)(R13), F13 + MOVD (32+8*7)(R13), F14 + MOVD (32+8*8)(R13), F15 +skipfprest: MOVW 12(R13), R4 MOVW 16(R13), R5 MOVW 20(R13), R6 diff --git a/src/runtime/rt0_linux_ppc64le.s b/src/runtime/rt0_linux_ppc64le.s index ac7b9225a43bef..2c5541357f2af7 100644 --- a/src/runtime/rt0_linux_ppc64le.s +++ b/src/runtime/rt0_linux_ppc64le.s @@ -4,6 +4,135 @@ TEXT _rt0_ppc64le_linux(SB),NOSPLIT,$0 BR _main<>(SB) +TEXT _rt0_ppc64le_linux_lib(SB),NOSPLIT,$-8 + // Start with standard C stack frame layout and linkage. + MOVD LR, R0 + MOVD R0, 16(R1) // Save LR in caller's frame. + MOVD R2, 24(R1) // Save TOC in caller's frame. + MOVDU R1, -320(R1) // Allocate frame. + + // Preserve callee-save registers. + MOVD R14, 24(R1) + MOVD R15, 32(R1) + MOVD R16, 40(R1) + MOVD R17, 48(R1) + MOVD R18, 56(R1) + MOVD R19, 64(R1) + MOVD R20, 72(R1) + MOVD R21, 80(R1) + MOVD R22, 88(R1) + MOVD R23, 96(R1) + MOVD R24, 104(R1) + MOVD R25, 112(R1) + MOVD R26, 120(R1) + MOVD R27, 128(R1) + MOVD R28, 136(R1) + MOVD R29, 144(R1) + MOVD g, 152(R1) // R30 + MOVD R31, 160(R1) + FMOVD F14, 168(R1) + FMOVD F15, 176(R1) + FMOVD F16, 184(R1) + FMOVD F17, 192(R1) + FMOVD F18, 200(R1) + FMOVD F19, 208(R1) + FMOVD F20, 216(R1) + FMOVD F21, 224(R1) + FMOVD F22, 232(R1) + FMOVD F23, 240(R1) + FMOVD F24, 248(R1) + FMOVD F25, 256(R1) + FMOVD F26, 264(R1) + FMOVD F27, 272(R1) + FMOVD F28, 280(R1) + FMOVD F29, 288(R1) + FMOVD F30, 296(R1) + FMOVD F31, 304(R1) + + MOVD R3, _rt0_ppc64le_linux_lib_argc<>(SB) + MOVD R4, _rt0_ppc64le_linux_lib_argv<>(SB) + + // Synchronous initialization. + MOVD $runtime·libpreinit(SB), R12 + MOVD R12, CTR + BL (CTR) + + // Create a new thread to do the runtime initialization and return. + MOVD _cgo_sys_thread_create(SB), R12 + CMP $0, R12 + BEQ nocgo + MOVD $_rt0_ppc64le_linux_lib_go(SB), R3 + MOVD $0, R4 + MOVD R12, CTR + BL (CTR) + BR done + +nocgo: + MOVD $0x800000, R12 // stacksize = 8192KB + MOVD R12, 8(R1) + MOVD $_rt0_ppc64le_linux_lib_go(SB), R12 + MOVD R12, 16(R1) + MOVD $runtime·newosproc0(SB),R12 + MOVD R12, CTR + BL (CTR) + +done: + // Restore saved registers. + MOVD 24(R1), R14 + MOVD 32(R1), R15 + MOVD 40(R1), R16 + MOVD 48(R1), R17 + MOVD 56(R1), R18 + MOVD 64(R1), R19 + MOVD 72(R1), R20 + MOVD 80(R1), R21 + MOVD 88(R1), R22 + MOVD 96(R1), R23 + MOVD 104(R1), R24 + MOVD 112(R1), R25 + MOVD 120(R1), R26 + MOVD 128(R1), R27 + MOVD 136(R1), R28 + MOVD 144(R1), R29 + MOVD 152(R1), g // R30 + MOVD 160(R1), R31 + FMOVD 168(R1), F14 + FMOVD 176(R1), F15 + FMOVD 184(R1), F16 + FMOVD 192(R1), F17 + FMOVD 200(R1), F18 + FMOVD 208(R1), F19 + FMOVD 216(R1), F20 + FMOVD 224(R1), F21 + FMOVD 232(R1), F22 + FMOVD 240(R1), F23 + FMOVD 248(R1), F24 + FMOVD 256(R1), F25 + FMOVD 264(R1), F26 + FMOVD 272(R1), F27 + FMOVD 280(R1), F28 + FMOVD 288(R1), F29 + FMOVD 296(R1), F30 + FMOVD 304(R1), F31 + + ADD $320, R1 + MOVD 24(R1), R2 + MOVD 16(R1), R0 + MOVD R0, LR + RET + +TEXT _rt0_ppc64le_linux_lib_go(SB),NOSPLIT,$0 + MOVD _rt0_ppc64le_linux_lib_argc<>(SB), R3 + MOVD _rt0_ppc64le_linux_lib_argv<>(SB), R4 + MOVD $runtime·rt0_go(SB), R12 + MOVD R12, CTR + BR (CTR) + +DATA _rt0_ppc64le_linux_lib_argc<>(SB)/8, $0 +GLOBL _rt0_ppc64le_linux_lib_argc<>(SB),NOPTR, $8 +DATA _rt0_ppc64le_linux_lib_argv<>(SB)/8, $0 +GLOBL _rt0_ppc64le_linux_lib_argv<>(SB),NOPTR, $8 + TEXT _main<>(SB),NOSPLIT,$-8 // In a statically linked binary, the stack contains argc, // argv as argc string pointers followed by a NULL, envv as a diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 4f82646dbbe7f7..aabe52da3c4231 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -1,3 +1,7 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package runtime_test import ( @@ -98,7 +102,9 @@ func TestGdbPython(t *testing.T) { args := []string{"-nx", "-q", "--batch", "-iex", fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()), + "-ex", "set startup-with-shell off", "-ex", "info auto-load python-scripts", + "-ex", "set python print-stack full", "-ex", "br main.go:10", "-ex", "run", "-ex", "echo BEGIN info goroutines\n", @@ -205,6 +211,10 @@ func TestGdbBacktrace(t *testing.T) { checkGdbEnvironment(t) checkGdbVersion(t) + if runtime.GOOS == "netbsd" { + testenv.SkipFlaky(t, 15603) + } + dir, err := ioutil.TempDir("", "go-build") if err != nil { t.Fatalf("failed to create temp directory: %v", err) @@ -226,6 +236,7 @@ func TestGdbBacktrace(t *testing.T) { // Execute gdb commands. args := []string{"-nx", "-batch", + "-ex", "set startup-with-shell off", "-ex", "break main.eee", "-ex", "run", "-ex", "backtrace", diff --git a/src/runtime/runtime-lldb_test.go b/src/runtime/runtime-lldb_test.go index 2bd91c1ec03893..4c379b9cdc28b7 100644 --- a/src/runtime/runtime-lldb_test.go +++ b/src/runtime/runtime-lldb_test.go @@ -232,7 +232,7 @@ func verifyAranges(t *testing.T, byteorder binary.ByteOrder, data io.ReadSeeker) SegmentSize uint8 } for { - offset, err := data.Seek(0, 1) + offset, err := data.Seek(0, io.SeekCurrent) if err != nil { t.Fatalf("Seek error: %v", err) } @@ -246,7 +246,7 @@ func verifyAranges(t *testing.T, byteorder binary.ByteOrder, data io.ReadSeeker) if lastTupleOffset%tupleSize != 0 { t.Fatalf("Invalid arange length %d, (addr %d, seg %d)", header.UnitLength, header.AddressSize, header.SegmentSize) } - if _, err = data.Seek(lastTupleOffset, 0); err != nil { + if _, err = data.Seek(lastTupleOffset, io.SeekStart); err != nil { t.Fatalf("Seek error: %v", err) } buf := make([]byte, tupleSize) diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index e19c7fe93248bc..3c9eed5905c53e 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -508,7 +508,7 @@ func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { // reflect_addReflectOff adds a pointer to the reflection offset lookup map. //go:linkname reflect_addReflectOff reflect.addReflectOff func reflect_addReflectOff(ptr unsafe.Pointer) int32 { - lock(&reflectOffs.lock) + reflectOffsLock() if reflectOffs.m == nil { reflectOffs.m = make(map[int32]unsafe.Pointer) reflectOffs.minv = make(map[unsafe.Pointer]int32) @@ -521,6 +521,6 @@ func reflect_addReflectOff(ptr unsafe.Pointer) int32 { reflectOffs.m[id] = ptr reflectOffs.minv[ptr] = id } - unlock(&reflectOffs.lock) + reflectOffsUnlock() return id } diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 71da504f1c0e19..6119e75203c457 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -725,7 +725,8 @@ var ( support_avx bool support_avx2 bool - goarm uint8 // set by cmd/link on arm systems + goarm uint8 // set by cmd/link on arm systems + framepointer_enabled bool // set by cmd/link ) // Set by the linker so the runtime can determine the buildmode. diff --git a/src/runtime/signal1_unix.go b/src/runtime/signal1_unix.go index 31c1f2c4e58e93..5080202833e3d8 100644 --- a/src/runtime/signal1_unix.go +++ b/src/runtime/signal1_unix.go @@ -193,7 +193,17 @@ func dieFromSignal(sig int32) { setsig(sig, _SIG_DFL, false) updatesigmask(sigmask{}) raise(sig) - // That should have killed us; call exit just in case. + + // That should have killed us. On some systems, though, raise + // sends the signal to the whole process rather than to just + // the current thread, which means that the signal may not yet + // have been delivered. Give other threads a chance to run and + // pick up the signal. + osyield() + osyield() + osyield() + + // If we are still somehow running, just exit with the wrong status. exit(2) } diff --git a/src/runtime/softfloat_arm.go b/src/runtime/softfloat_arm.go index 648b2e1169cffb..5f609c80d3ebae 100644 --- a/src/runtime/softfloat_arm.go +++ b/src/runtime/softfloat_arm.go @@ -530,7 +530,7 @@ execute: case 0xeeb80ac0: // D[regd] = S[regm] (MOVWF) cmp := int32(m.freglo[regm]) if cmp < 0 { - fputf(regd, f64to32(fintto64(int64(-cmp)))) + fputf(regd, f64to32(fintto64(-int64(cmp)))) m.freglo[regd] ^= 0x80000000 } else { fputf(regd, f64to32(fintto64(int64(cmp)))) @@ -552,7 +552,7 @@ execute: case 0xeeb80bc0: // D[regd] = S[regm] (MOVWD) cmp := int32(m.freglo[regm]) if cmp < 0 { - fputd(regd, fintto64(int64(-cmp))) + fputd(regd, fintto64(-int64(cmp))) m.freghi[regd] ^= 0x80000000 } else { fputd(regd, fintto64(int64(cmp))) diff --git a/src/runtime/stack.go b/src/runtime/stack.go index f68c513fd67e39..ee2797e1443300 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -155,9 +155,6 @@ var stackLarge struct { free [_MHeapMap_Bits]mSpanList // free lists by log_2(s.npages) } -// Cached value of haveexperiment("framepointer") -var framepointer_enabled bool - func stackinit() { if _StackCacheSize&_PageMask != 0 { throw("cache size must be a multiple of page size") @@ -254,6 +251,8 @@ func stackpoolfree(x gclinkptr, order uint8) { // stackcacherefill/stackcacherelease implement a global pool of stack segments. // The pool is required to prevent unlimited growth of per-thread caches. +// +//go:systemstack func stackcacherefill(c *mcache, order uint8) { if stackDebug >= 1 { print("stackcacherefill order=", order, "\n") @@ -275,6 +274,7 @@ func stackcacherefill(c *mcache, order uint8) { c.stackcache[order].size = size } +//go:systemstack func stackcacherelease(c *mcache, order uint8) { if stackDebug >= 1 { print("stackcacherelease order=", order, "\n") @@ -293,6 +293,7 @@ func stackcacherelease(c *mcache, order uint8) { c.stackcache[order].size = size } +//go:systemstack func stackcache_clear(c *mcache) { if stackDebug >= 1 { print("stackcache clear\n") @@ -311,6 +312,12 @@ func stackcache_clear(c *mcache) { unlock(&stackpoolmu) } +// stackalloc allocates an n byte stack. +// +// stackalloc must run on the system stack because it uses per-P +// resources and must not split the stack. +// +//go:systemstack func stackalloc(n uint32) (stack, []stkbar) { // Stackalloc must be called on scheduler stack, so that we // never try to grow the stack during the code that stackalloc runs. @@ -408,6 +415,12 @@ func stackalloc(n uint32) (stack, []stkbar) { return stack{uintptr(v), uintptr(v) + top}, *(*[]stkbar)(unsafe.Pointer(&stkbarSlice)) } +// stackfree frees an n byte stack allocation at stk. +// +// stackfree must run on the system stack because it uses per-P +// resources and must not split the stack. +// +//go:systemstack func stackfree(stk stack, n uintptr) { gp := getg() v := unsafe.Pointer(stk.lo) @@ -1010,7 +1023,13 @@ func newstack() { // return. } if !gp.gcscandone { - scanstack(gp) + // gcw is safe because we're on the + // system stack. + gcw := &gp.m.p.ptr().gcw + scanstack(gp, gcw) + if gcBlackenPromptly { + gcw.dispose() + } gp.gcscandone = true } gp.preemptscan = false diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s index f80a85fb67767b..2bb818f456ea2e 100644 --- a/src/runtime/sys_openbsd_386.s +++ b/src/runtime/sys_openbsd_386.s @@ -214,14 +214,6 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12 MOVL context+8(FP), BX MOVL BX, 8(SP) CALL runtime·sigtrampgo(SB) - - // call sigreturn - MOVL context+8(FP), AX - MOVL $0, 0(SP) // syscall gap - MOVL AX, 4(SP) // arg 1 - sigcontext - MOVL $103, AX // sys_sigreturn - INT $0x80 - MOVL $0xf1, 0xf1 // crash RET // int32 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void)); diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s index d550a818ce85f1..9c197379fb8f42 100644 --- a/src/runtime/sys_windows_amd64.s +++ b/src/runtime/sys_windows_amd64.s @@ -11,7 +11,7 @@ #define maxargs 16 // void runtime·asmstdcall(void *c); -TEXT runtime·asmstdcall(SB),NOSPLIT,$0 +TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 // asmcgocall will put first argument into CX. PUSHQ CX // save for later MOVQ libcall_fn(CX), AX @@ -62,7 +62,7 @@ loadregs: RET -TEXT runtime·badsignal2(SB),NOSPLIT,$48 +TEXT runtime·badsignal2(SB),NOSPLIT|NOFRAME,$48 // stderr MOVQ $-12, CX // stderr MOVQ CX, 0(SP) @@ -102,7 +102,7 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0 // exception record and context pointers. // Handler function is stored in AX. // Return 0 for 'not handled', -1 for handled. -TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 +TEXT runtime·sigtramp(SB),NOSPLIT|NOFRAME,$0-0 // CX: PEXCEPTION_POINTERS ExceptionInfo // DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved @@ -190,32 +190,32 @@ done: RET -TEXT runtime·exceptiontramp(SB),NOSPLIT,$0 +TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 MOVQ $runtime·exceptionhandler(SB), AX JMP runtime·sigtramp(SB) -TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0 +TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0 MOVQ $runtime·firstcontinuehandler(SB), AX JMP runtime·sigtramp(SB) -TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0 +TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0 MOVQ $runtime·lastcontinuehandler(SB), AX JMP runtime·sigtramp(SB) -TEXT runtime·ctrlhandler(SB),NOSPLIT,$8 +TEXT runtime·ctrlhandler(SB),NOSPLIT|NOFRAME,$8 MOVQ CX, 16(SP) // spill MOVQ $runtime·ctrlhandler1(SB), CX MOVQ CX, 0(SP) CALL runtime·externalthreadhandler(SB) RET -TEXT runtime·profileloop(SB),NOSPLIT,$8 +TEXT runtime·profileloop(SB),NOSPLIT|NOFRAME,$8 MOVQ $runtime·profileloop1(SB), CX MOVQ CX, 0(SP) CALL runtime·externalthreadhandler(SB) RET -TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0 +TEXT runtime·externalthreadhandler(SB),NOSPLIT|NOFRAME,$0 PUSHQ BP MOVQ SP, BP PUSHQ BX @@ -228,7 +228,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0 SUBQ $m__size, SP // space for M MOVQ SP, 0(SP) MOVQ $m__size, 8(SP) - CALL runtime·memclr(SB) // smashes AX,BX,CX + CALL runtime·memclr(SB) // smashes AX,BX,CX, maybe BP LEAQ m_tls(SP), CX MOVQ CX, 0x28(GS) @@ -239,7 +239,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0 MOVQ SP, 0(SP) MOVQ $g__size, 8(SP) - CALL runtime·memclr(SB) // smashes AX,BX,CX + CALL runtime·memclr(SB) // smashes AX,BX,CX, maybe BP LEAQ g__size(SP), BX MOVQ BX, g_m(SP) @@ -430,7 +430,7 @@ ret: // Runs on OS stack. duration (in 100ns units) is in BX. // The function leaves room for 4 syscall parameters // (as per windows amd64 calling convention). -TEXT runtime·usleep2(SB),NOSPLIT,$48 +TEXT runtime·usleep2(SB),NOSPLIT|NOFRAME,$48 MOVQ SP, AX ANDQ $~15, SP // alignment as per Windows requirement MOVQ AX, 40(SP) @@ -446,7 +446,7 @@ TEXT runtime·usleep2(SB),NOSPLIT,$48 RET // Runs on OS stack. -TEXT runtime·switchtothread(SB),NOSPLIT,$0 +TEXT runtime·switchtothread(SB),NOSPLIT|NOFRAME,$0 MOVQ SP, AX ANDQ $~15, SP // alignment as per Windows requirement SUBQ $(48), SP // room for SP and 4 args as per Windows requirement diff --git a/src/runtime/testdata/testprog/gc.go b/src/runtime/testdata/testprog/gc.go index 0676e9a4eccd2a..a0c1f82b56bd62 100644 --- a/src/runtime/testdata/testprog/gc.go +++ b/src/runtime/testdata/testprog/gc.go @@ -8,11 +8,14 @@ import ( "fmt" "os" "runtime" + "runtime/debug" + "sync/atomic" "time" ) func init() { register("GCFairness", GCFairness) + register("GCFairness2", GCFairness2) register("GCSys", GCSys) } @@ -72,3 +75,35 @@ func GCFairness() { time.Sleep(10 * time.Millisecond) fmt.Println("OK") } + +func GCFairness2() { + // Make sure user code can't exploit the GC's high priority + // scheduling to make scheduling of user code unfair. See + // issue #15706. + runtime.GOMAXPROCS(1) + debug.SetGCPercent(1) + var count [3]int64 + var sink [3]interface{} + for i := range count { + go func(i int) { + for { + sink[i] = make([]byte, 1024) + atomic.AddInt64(&count[i], 1) + } + }(i) + } + // Note: If the unfairness is really bad, it may not even get + // past the sleep. + // + // If the scheduling rules change, this may not be enough time + // to let all goroutines run, but for now we cycle through + // them rapidly. + time.Sleep(30 * time.Millisecond) + for i := range count { + if atomic.LoadInt64(&count[i]) == 0 { + fmt.Printf("goroutine %d did not run\n", i) + return + } + } + fmt.Println("OK") +} diff --git a/src/runtime/testdata/testprog/signal.go b/src/runtime/testdata/testprog/signal.go index 7926908828ba85..2ccbada57b3c1a 100644 --- a/src/runtime/testdata/testprog/signal.go +++ b/src/runtime/testdata/testprog/signal.go @@ -6,7 +6,10 @@ package main -import "syscall" +import ( + "syscall" + "time" +) func init() { register("SignalExitStatus", SignalExitStatus) @@ -14,4 +17,13 @@ func init() { func SignalExitStatus() { syscall.Kill(syscall.Getpid(), syscall.SIGTERM) + + // Should die immediately, but we've seen flakiness on various + // systems (see issue 14063). It's possible that the signal is + // being delivered to a different thread and we are returning + // and exiting before that thread runs again. Give the program + // a little while to die to make sure we pick up the signal + // before we return and exit the program. The time here + // shouldn't matter--we'll never really sleep this long. + time.Sleep(time.Second) } diff --git a/src/runtime/testdata/testprogcgo/threadpanic_windows.c b/src/runtime/testdata/testprogcgo/threadpanic_windows.c index 6f896634a6d946..ba66d0f5c9564f 100644 --- a/src/runtime/testdata/testprogcgo/threadpanic_windows.c +++ b/src/runtime/testdata/testprogcgo/threadpanic_windows.c @@ -8,7 +8,7 @@ void gopanic(void); -static unsigned int +static unsigned int __attribute__((__stdcall__)) die(void* x) { gopanic(); diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 7771426ef95e97..279fb52fc0c4e3 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -241,6 +241,11 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // stk is the stack containing sp. // The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp. f = frame.fn + if f.pcsp == 0 { + // No frame information, must be external function, like race support. + // See golang.org/issue/13568. + break + } // Found an actual function. // Derive frame pointer and link register. @@ -251,6 +256,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in sp := frame.sp if flags&_TraceJumpStack != 0 && f.entry == systemstackPC && gp == g.m.g0 && gp.m.curg != nil { sp = gp.m.curg.sched.sp + frame.sp = sp stkbarG = gp.m.curg stkbar = stkbarG.stkbar[stkbarG.stkbarPos:] cgoCtxt = gp.m.curg.cgoCtxt @@ -1034,7 +1040,7 @@ func printOneCgoTraceback(pc uintptr, max int, arg *cgoSymbolizerArg) int { if arg.file != nil { print(gostringnocopy(arg.file), ":", arg.lineno, " ") } - print("pc=", hex(c), "\n") + print("pc=", hex(pc), "\n") c++ if arg.more == 0 { break diff --git a/src/runtime/type.go b/src/runtime/type.go index 608c601abd2973..d7ec5573a9d851 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -169,6 +169,20 @@ var reflectOffs struct { minv map[unsafe.Pointer]int32 } +func reflectOffsLock() { + lock(&reflectOffs.lock) + if raceenabled { + raceacquire(unsafe.Pointer(&reflectOffs.lock)) + } +} + +func reflectOffsUnlock() { + if raceenabled { + racerelease(unsafe.Pointer(&reflectOffs.lock)) + } + unlock(&reflectOffs.lock) +} + func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { if off == 0 { return name{} @@ -182,9 +196,9 @@ func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { } } if md == nil { - lock(&reflectOffs.lock) + reflectOffsLock() res, found := reflectOffs.m[int32(off)] - unlock(&reflectOffs.lock) + reflectOffsUnlock() if !found { println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:") for next := &firstmoduledata; next != nil; next = next.next { @@ -219,9 +233,9 @@ func (t *_type) typeOff(off typeOff) *_type { } } if md == nil { - lock(&reflectOffs.lock) + reflectOffsLock() res := reflectOffs.m[int32(off)] - unlock(&reflectOffs.lock) + reflectOffsUnlock() if res == nil { println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:") for next := &firstmoduledata; next != nil; next = next.next { @@ -252,9 +266,9 @@ func (t *_type) textOff(off textOff) unsafe.Pointer { } } if md == nil { - lock(&reflectOffs.lock) + reflectOffsLock() res := reflectOffs.m[int32(off)] - unlock(&reflectOffs.lock) + reflectOffsUnlock() if res == nil { println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:") for next := &firstmoduledata; next != nil; next = next.next { diff --git a/src/strings/reader.go b/src/strings/reader.go index e254837c63b96a..6c1a5064c0d5ef 100644 --- a/src/strings/reader.go +++ b/src/strings/reader.go @@ -107,11 +107,11 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) { r.prevRune = -1 var abs int64 switch whence { - case 0: + case io.SeekStart: abs = offset - case 1: + case io.SeekCurrent: abs = r.i + offset - case 2: + case io.SeekEnd: abs = int64(len(r.s)) + offset default: return 0, errors.New("strings.Reader.Seek: invalid whence") diff --git a/src/strings/strings_amd64.go b/src/strings/strings_amd64.go index 55bf2d2f6fc77a..91b29ce358fa6d 100644 --- a/src/strings/strings_amd64.go +++ b/src/strings/strings_amd64.go @@ -7,7 +7,7 @@ package strings // indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s. // indexShortStr requires 2 <= len(c) <= shortStringLen func indexShortStr(s, c string) int // ../runtime/asm_$GOARCH.s -const shortStringLen = 31 +const shortStringLen = 16 // TODO: restore to 31 when #15679 is fixed // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. func Index(s, sep string) int { diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go index 1ed803bf8502e7..6bd6fb5443d31f 100644 --- a/src/strings/strings_test.go +++ b/src/strings/strings_test.go @@ -952,7 +952,7 @@ var UnreadRuneErrorTests = []struct { {"Read", func(r *Reader) { r.Read([]byte{0}) }}, {"ReadByte", func(r *Reader) { r.ReadByte() }}, {"UnreadRune", func(r *Reader) { r.UnreadRune() }}, - {"Seek", func(r *Reader) { r.Seek(0, 1) }}, + {"Seek", func(r *Reader) { r.Seek(0, io.SeekCurrent) }}, {"WriteTo", func(r *Reader) { r.WriteTo(&bytes.Buffer{}) }}, } @@ -1036,6 +1036,70 @@ var ContainsTests = []struct { {"abc", "bcd", false}, {"abc", "", true}, {"", "a", false}, + + // cases to cover code in runtime/asm_amd64.s:indexShortStr + // 2-byte needle + {"xxxxxx", "01", false}, + {"01xxxx", "01", true}, + {"xx01xx", "01", true}, + {"xxxx01", "01", true}, + {"01xxxxx"[1:], "01", false}, + {"xxxxx01"[:6], "01", false}, + // 3-byte needle + {"xxxxxxx", "012", false}, + {"012xxxx", "012", true}, + {"xx012xx", "012", true}, + {"xxxx012", "012", true}, + {"012xxxxx"[1:], "012", false}, + {"xxxxx012"[:7], "012", false}, + // 4-byte needle + {"xxxxxxxx", "0123", false}, + {"0123xxxx", "0123", true}, + {"xx0123xx", "0123", true}, + {"xxxx0123", "0123", true}, + {"0123xxxxx"[1:], "0123", false}, + {"xxxxx0123"[:8], "0123", false}, + // 5-7-byte needle + {"xxxxxxxxx", "01234", false}, + {"01234xxxx", "01234", true}, + {"xx01234xx", "01234", true}, + {"xxxx01234", "01234", true}, + {"01234xxxxx"[1:], "01234", false}, + {"xxxxx01234"[:9], "01234", false}, + // 8-byte needle + {"xxxxxxxxxxxx", "01234567", false}, + {"01234567xxxx", "01234567", true}, + {"xx01234567xx", "01234567", true}, + {"xxxx01234567", "01234567", true}, + {"01234567xxxxx"[1:], "01234567", false}, + {"xxxxx01234567"[:12], "01234567", false}, + // 9-15-byte needle + {"xxxxxxxxxxxxx", "012345678", false}, + {"012345678xxxx", "012345678", true}, + {"xx012345678xx", "012345678", true}, + {"xxxx012345678", "012345678", true}, + {"012345678xxxxx"[1:], "012345678", false}, + {"xxxxx012345678"[:13], "012345678", false}, + // 16-byte needle + {"xxxxxxxxxxxxxxxxxxxx", "0123456789ABCDEF", false}, + {"0123456789ABCDEFxxxx", "0123456789ABCDEF", true}, + {"xx0123456789ABCDEFxx", "0123456789ABCDEF", true}, + {"xxxx0123456789ABCDEF", "0123456789ABCDEF", true}, + {"0123456789ABCDEFxxxxx"[1:], "0123456789ABCDEF", false}, + {"xxxxx0123456789ABCDEF"[:20], "0123456789ABCDEF", false}, + // 17-31-byte needle + {"xxxxxxxxxxxxxxxxxxxxx", "0123456789ABCDEFG", false}, + {"0123456789ABCDEFGxxxx", "0123456789ABCDEFG", true}, + {"xx0123456789ABCDEFGxx", "0123456789ABCDEFG", true}, + {"xxxx0123456789ABCDEFG", "0123456789ABCDEFG", true}, + {"0123456789ABCDEFGxxxxx"[1:], "0123456789ABCDEFG", false}, + {"xxxxx0123456789ABCDEFG"[:21], "0123456789ABCDEFG", false}, + + // partial match cases + {"xx01x", "012", false}, // 3 + {"xx0123x", "01234", false}, // 5-7 + {"xx01234567x", "012345678", false}, // 9-15 + {"xx0123456789ABCDEFx", "0123456789ABCDEFG", false}, // 17-31, issue 15679 } func TestContains(t *testing.T) { diff --git a/src/sync/atomic/value.go b/src/sync/atomic/value.go index ab3aa11285478f..30abf726344e96 100644 --- a/src/sync/atomic/value.go +++ b/src/sync/atomic/value.go @@ -12,7 +12,11 @@ import ( // Values can be created as part of other data structures. // The zero value for a Value returns nil from Load. // Once Store has been called, a Value must not be copied. +// +// A Value must not be copied after first use. type Value struct { + noCopy noCopy + v interface{} } @@ -83,3 +87,13 @@ func (v *Value) Store(x interface{}) { // Disable/enable preemption, implemented in runtime. func runtime_procPin() func runtime_procUnpin() + +// noCopy may be embedded into structs which must not be copied +// after the first use. +// +// See https://github.com/golang/go/issues/8005#issuecomment-190753527 +// for details. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} diff --git a/src/sync/cond.go b/src/sync/cond.go index f711c39da2d856..c070d9d84ef9e5 100644 --- a/src/sync/cond.go +++ b/src/sync/cond.go @@ -20,6 +20,8 @@ import ( // A Cond can be created as part of other structures. // A Cond must not be copied after first use. type Cond struct { + noCopy noCopy + // L is held while observing or changing the condition L Locker @@ -84,3 +86,13 @@ func (c *copyChecker) check() { panic("sync.Cond is copied") } } + +// noCopy may be embedded into structs which must not be copied +// after the first use. +// +// See https://github.com/golang/go/issues/8005#issuecomment-190753527 +// for details. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} diff --git a/src/sync/mutex.go b/src/sync/mutex.go index 78b115cf5a1972..90892793f0a99c 100644 --- a/src/sync/mutex.go +++ b/src/sync/mutex.go @@ -19,6 +19,8 @@ import ( // A Mutex is a mutual exclusion lock. // Mutexes can be created as part of other structures; // the zero value for a Mutex is an unlocked mutex. +// +// A Mutex must not be copied after first use. type Mutex struct { state int32 sema uint32 diff --git a/src/sync/pool.go b/src/sync/pool.go index 2acf505f3c96b9..bf29d88c5cb6cc 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -40,7 +40,10 @@ import ( // that scenario. It is more efficient to have such objects implement their own // free list. // +// A Pool must not be copied after first use. type Pool struct { + noCopy noCopy + local unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal localSize uintptr // size of the local array diff --git a/src/sync/rwmutex.go b/src/sync/rwmutex.go index 9fc6e3bd2c5151..455d412330034e 100644 --- a/src/sync/rwmutex.go +++ b/src/sync/rwmutex.go @@ -16,6 +16,8 @@ import ( // RWMutexes can be created as part of other // structures; the zero value for a RWMutex is // an unlocked mutex. +// +// An RWMutex must not be copied after first use. type RWMutex struct { w Mutex // held if there are pending writers writerSem uint32 // semaphore for writers to wait for completing readers diff --git a/src/sync/waitgroup.go b/src/sync/waitgroup.go index 029e6077cde714..b386e1fec2b61a 100644 --- a/src/sync/waitgroup.go +++ b/src/sync/waitgroup.go @@ -15,7 +15,11 @@ import ( // goroutines to wait for. Then each of the goroutines // runs and calls Done when finished. At the same time, // Wait can be used to block until all goroutines have finished. +// +// A WaitGroup must not be copied after first use. type WaitGroup struct { + noCopy noCopy + // 64-bit value: high 32 bits are counter, low 32 bits are waiter count. // 64-bit atomic operations require 64-bit alignment, but 32-bit // compilers do not ensure it. So we allocate 12 bytes and then use diff --git a/src/syscall/bpf_bsd.go b/src/syscall/bpf_bsd.go index 2523e9b001a560..8b587559edbcba 100644 --- a/src/syscall/bpf_bsd.go +++ b/src/syscall/bpf_bsd.go @@ -12,14 +12,17 @@ import ( "unsafe" ) +// Deprecated: Use golang.org/x/net/bpf instead. func BpfStmt(code, k int) *BpfInsn { return &BpfInsn{Code: uint16(code), K: uint32(k)} } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfJump(code, k, jt, jf int) *BpfInsn { return &BpfInsn{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfBuflen(fd int) (int, error) { var l int _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l))) @@ -29,6 +32,7 @@ func BpfBuflen(fd int) (int, error) { return l, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfBuflen(fd, l int) (int, error) { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l))) if err != 0 { @@ -37,6 +41,7 @@ func SetBpfBuflen(fd, l int) (int, error) { return l, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfDatalink(fd int) (int, error) { var t int _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t))) @@ -46,6 +51,7 @@ func BpfDatalink(fd int) (int, error) { return t, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfDatalink(fd, t int) (int, error) { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t))) if err != 0 { @@ -54,6 +60,7 @@ func SetBpfDatalink(fd, t int) (int, error) { return t, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfPromisc(fd, m int) error { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m))) if err != 0 { @@ -62,6 +69,7 @@ func SetBpfPromisc(fd, m int) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func FlushBpf(fd int) error { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0) if err != 0 { @@ -75,6 +83,7 @@ type ivalue struct { value int16 } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfInterface(fd int, name string) (string, error) { var iv ivalue _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv))) @@ -84,6 +93,7 @@ func BpfInterface(fd int, name string) (string, error) { return name, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfInterface(fd int, name string) error { var iv ivalue copy(iv.name[:], []byte(name)) @@ -94,6 +104,7 @@ func SetBpfInterface(fd int, name string) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfTimeout(fd int) (*Timeval, error) { var tv Timeval _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv))) @@ -103,6 +114,7 @@ func BpfTimeout(fd int) (*Timeval, error) { return &tv, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfTimeout(fd int, tv *Timeval) error { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv))) if err != 0 { @@ -111,6 +123,7 @@ func SetBpfTimeout(fd int, tv *Timeval) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfStats(fd int) (*BpfStat, error) { var s BpfStat _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s))) @@ -120,6 +133,7 @@ func BpfStats(fd int) (*BpfStat, error) { return &s, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfImmediate(fd, m int) error { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m))) if err != 0 { @@ -128,6 +142,7 @@ func SetBpfImmediate(fd, m int) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpf(fd int, i []BpfInsn) error { var p BpfProgram p.Len = uint32(len(i)) @@ -139,6 +154,7 @@ func SetBpf(fd int, i []BpfInsn) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func CheckBpfVersion(fd int) error { var v BpfVersion _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v))) @@ -151,6 +167,7 @@ func CheckBpfVersion(fd int) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func BpfHeadercmpl(fd int) (int, error) { var f int _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f))) @@ -160,6 +177,7 @@ func BpfHeadercmpl(fd int) (int, error) { return f, nil } +// Deprecated: Use golang.org/x/net/bpf instead. func SetBpfHeadercmpl(fd, f int) error { _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f))) if err != 0 { diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index e49bad75b284ff..5a6b2049970901 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -32,6 +32,7 @@ type SysProcAttr struct { Pgid int // Child's process group ID if Setpgid. Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only) Cloneflags uintptr // Flags for clone calls (Linux only) + Unshare uintptr // Flags for unshare calls (Linux only) UidMappings []SysProcIDMap // User ID mappings for user namespaces. GidMappings []SysProcIDMap // Group ID mappings for user namespaces. // GidMappingsEnableSetgroups enabling setgroups syscall. @@ -194,6 +195,14 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } + // Unshare + if sys.Unshare != 0 { + _, _, err1 = RawSyscall(SYS_UNSHARE, sys.Unshare, 0, 0) + if err1 != 0 { + goto childerror + } + } + // User and groups if cred := sys.Credential; cred != nil { ngroups := uintptr(len(cred.Groups)) diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index eb32cfd4b1e46c..099756328c9cb4 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -125,3 +125,39 @@ func TestEmptyCredGroupsDisableSetgroups(t *testing.T) { t.Fatal(err) } } + +func TestUnshare(t *testing.T) { + // Make sure we are running as root so we have permissions to use unshare + // and create a network namespace. + if os.Getuid() != 0 { + t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") + } + + // When running under the Go continuous build, skip tests for + // now when under Kubernetes. (where things are root but not quite) + // Both of these are our own environment variables. + // See Issue 12815. + if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { + t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") + } + + cmd := exec.Command("cat", "/proc/net/dev") + cmd.SysProcAttr = &syscall.SysProcAttr{ + Unshare: syscall.CLONE_NEWNET, + } + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Cmd failed with err %v, output: %s", err, out) + } + + // Check there is only the local network interface + sout := strings.TrimSpace(string(out)) + if !strings.Contains(sout, "lo:") { + t.Fatalf("Expected lo network interface to exist, got %s", sout) + } + + lines := strings.Split(sout, "\n") + if len(lines) != 3 { + t.Fatalf("Expected 3 lines of output, got %d", len(lines)) + } +} diff --git a/src/syscall/exec_plan9.go b/src/syscall/exec_plan9.go index bccea5105cb7bb..6551bcb1c1f215 100644 --- a/src/syscall/exec_plan9.go +++ b/src/syscall/exec_plan9.go @@ -12,55 +12,44 @@ import ( "unsafe" ) -// Lock synchronizing creation of new file descriptors with fork. -// -// We want the child in a fork/exec sequence to inherit only the -// file descriptors we intend. To do that, we mark all file -// descriptors close-on-exec and then, in the child, explicitly -// unmark the ones we want the exec'ed program to keep. -// Unix doesn't make this easy: there is, in general, no way to -// allocate a new file descriptor close-on-exec. Instead you -// have to allocate the descriptor and then mark it close-on-exec. -// If a fork happens between those two events, the child's exec -// will inherit an unwanted file descriptor. -// -// This lock solves that race: the create new fd/mark close-on-exec -// operation is done holding ForkLock for reading, and the fork itself -// is done holding ForkLock for writing. At least, that's the idea. -// There are some complications. -// -// Some system calls that create new file descriptors can block -// for arbitrarily long times: open on a hung NFS server or named -// pipe, accept on a socket, and so on. We can't reasonably grab -// the lock across those operations. -// -// It is worse to inherit some file descriptors than others. -// If a non-malicious child accidentally inherits an open ordinary file, -// that's not a big deal. On the other hand, if a long-lived child -// accidentally inherits the write end of a pipe, then the reader -// of that pipe will not see EOF until that child exits, potentially -// causing the parent program to hang. This is a common problem -// in threaded C programs that use popen. -// -// Luckily, the file descriptors that are most important not to -// inherit are not the ones that can take an arbitrarily long time -// to create: pipe returns instantly, and the net package uses -// non-blocking I/O to accept on a listening socket. -// The rules for which file descriptor-creating operations use the -// ForkLock are as follows: -// -// 1) Pipe. Does not block. Use the ForkLock. -// 2) Socket. Does not block. Use the ForkLock. -// 3) Accept. If using non-blocking mode, use the ForkLock. -// Otherwise, live with the race. -// 4) Open. Can block. Use O_CLOEXEC if available (Linux). -// Otherwise, live with the race. -// 5) Dup. Does not block. Use the ForkLock. -// On Linux, could use fcntl F_DUPFD_CLOEXEC -// instead of the ForkLock, but only for dup(fd, -1). - +// ForkLock is not used on plan9. var ForkLock sync.RWMutex +// gstringb reads a non-empty string from b, prefixed with a 16-bit length in little-endian order. +// It returns the string as a byte slice, or nil if b is too short to contain the length or +// the full string. +//go:nosplit +func gstringb(b []byte) []byte { + if len(b) < 2 { + return nil + } + n, b := gbit16(b) + if int(n) > len(b) { + return nil + } + return b[:n] +} + +// Offset of the name field in a 9P directory entry - see UnmarshalDir() in dir_plan9.go +const nameOffset = 39 + +// gdirname returns the first filename from a buffer of directory entries, +// and a slice containing the remaining directory entries. +// If the buffer doesn't start with a valid directory entry, the returned name is nil. +//go:nosplit +func gdirname(buf []byte) (name []byte, rest []byte) { + if len(buf) < 2 { + return + } + size, buf := gbit16(buf) + if size < STATFIXLEN || int(size) > len(buf) { + return + } + name = gstringb(buf[nameOffset:size]) + rest = buf[size:] + return +} + // StringSlicePtr converts a slice of strings to a slice of pointers // to NUL-terminated byte arrays. If any string contains a NUL byte // this function panics instead of returning an error. @@ -104,64 +93,20 @@ func readdirnames(dirfd int) (names []string, err error) { if n == 0 { break } - for i := 0; i < n; { - m, _ := gbit16(buf[i:]) - m += 2 - - if m < STATFIXLEN { - return nil, ErrBadStat - } - - s, _, ok := gstring(buf[i+41:]) - if !ok { + for b := buf[:n]; len(b) > 0; { + var s []byte + s, b = gdirname(b) + if s == nil { return nil, ErrBadStat } - names = append(names, s) - i += int(m) + names = append(names, string(s)) } } return } -// readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d. -// ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read. -func readdupdevice() (fds []int, err error) { - dupdevfd, err := Open("#d", O_RDONLY) - if err != nil { - return - } - defer Close(dupdevfd) - - names, err := readdirnames(dupdevfd) - if err != nil { - return - } - - fds = make([]int, 0, len(names)/2) - for _, name := range names { - if n := len(name); n > 3 && name[n-3:n] == "ctl" { - continue - } - fd := int(atoi([]byte(name))) - switch fd { - case 0, 1, 2, dupdevfd: - continue - } - fds = append(fds, fd) - } - return -} - -var startupFds []int - -// Plan 9 does not allow clearing the OCEXEC flag -// from the underlying channel backing an open file descriptor, -// therefore we store a list of already opened file descriptors -// inside startupFds and skip them when manually closing descriptors -// not meant to be passed to a child exec. -func init() { - startupFds, _ = readdupdevice() -} +// name of the directory containing names and control files for all open file descriptors +var dupdev, _ = BytePtrFromString("#d") // forkAndExecInChild forks the process, calling dup onto 0..len(fd) // and finally invoking exec(argv0, argvv, envv) in the child. @@ -174,7 +119,7 @@ func init() { // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. //go:norace -func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int, rflag int) (pid int, err error) { +func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, pipe int, rflag int) (pid int, err error) { // Declare all variables at top in case any // declarations require heap allocation (e.g., errbuf). var ( @@ -184,6 +129,8 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at clearenv int envfd int errbuf [ERRMAX]byte + statbuf [STATMAX]byte + dupdevfd int ) // Guard against side effects of shuffling fds below. @@ -218,14 +165,39 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at // Fork succeeded, now in child. // Close fds we don't need. - for i = 0; i < len(fdsToClose); i++ { - if fdsToClose[i] != pipe { - RawSyscall(SYS_CLOSE, uintptr(fdsToClose[i]), 0, 0) + r1, _, _ = RawSyscall(SYS_OPEN, uintptr(unsafe.Pointer(dupdev)), uintptr(O_RDONLY), 0) + dupdevfd = int(r1) + if dupdevfd == -1 { + goto childerror + } +dirloop: + for { + r1, _, _ = RawSyscall6(SYS_PREAD, uintptr(dupdevfd), uintptr(unsafe.Pointer(&statbuf[0])), uintptr(len(statbuf)), ^uintptr(0), ^uintptr(0), 0) + n := int(r1) + switch n { + case -1: + goto childerror + case 0: + break dirloop + } + for b := statbuf[:n]; len(b) > 0; { + var s []byte + s, b = gdirname(b) + if s == nil { + copy(errbuf[:], ErrBadStat.Error()) + goto childerror1 + } + if s[len(s)-1] == 'l' { + // control file for descriptor is named ctl + continue + } + closeFdExcept(int(atoi(s)), pipe, dupdevfd, fd) } } + RawSyscall(SYS_CLOSE, uintptr(dupdevfd), 0, 0) + // Write new environment variables. if envv != nil { - // Write new environment variables. for i = 0; i < len(envv); i++ { r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666)) @@ -313,6 +285,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at childerror: // send error string on pipe RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&errbuf[0])), uintptr(len(errbuf)), 0) +childerror1: errbuf[len(errbuf)-1] = 0 i = 0 for i < len(errbuf) && errbuf[i] != 0 { @@ -332,6 +305,20 @@ childerror: panic("unreached") } +// close the numbered file descriptor, unless it is fd1, fd2, or a member of fds. +//go:nosplit +func closeFdExcept(n int, fd1 int, fd2 int, fds []int) { + if n == fd1 || n == fd2 { + return + } + for _, fd := range fds { + if n == fd { + return + } + } + RawSyscall(SYS_CLOSE, uintptr(n), 0, 0) +} + func cexecPipe(p []int) error { e := Pipe(p) if e != nil { @@ -430,62 +417,23 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) } } - // Acquire the fork lock to prevent other threads from creating new fds before we fork. - ForkLock.Lock() - - // get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child. - // no new fds can be created while we hold the ForkLock for writing. - openFds, e := readdupdevice() - if e != nil { - ForkLock.Unlock() - return 0, e - } - - fdsToClose := make([]int, 0, len(openFds)) - for _, fd := range openFds { - doClose := true - - // exclude files opened at startup. - for _, sfd := range startupFds { - if fd == sfd { - doClose = false - break - } - } - - // exclude files explicitly requested by the caller. - for _, rfd := range attr.Files { - if fd == int(rfd) { - doClose = false - break - } - } - - if doClose { - fdsToClose = append(fdsToClose, fd) - } - } - // Allocate child status pipe close on exec. - e = cexecPipe(p[:]) + e := cexecPipe(p[:]) if e != nil { return 0, e } - fdsToClose = append(fdsToClose, p[0]) // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, dir, attr, fdsToClose, p[1], sys.Rfork) + pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, dir, attr, p[1], sys.Rfork) if err != nil { if p[0] >= 0 { Close(p[0]) Close(p[1]) } - ForkLock.Unlock() return 0, err } - ForkLock.Unlock() // Read child error status from pipe. Close(p[1]) @@ -493,8 +441,10 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) Close(p[0]) if err != nil || n != 0 { - if n != 0 { + if n > 0 { err = NewError(string(errbuf[:n])) + } else if err == nil { + err = NewError("failed to read exec status") } // Child failed; wait for it to exit, to make sure diff --git a/src/syscall/fd_nacl.go b/src/syscall/fd_nacl.go index 715992b1bfc684..e559793c8b80e7 100644 --- a/src/syscall/fd_nacl.go +++ b/src/syscall/fd_nacl.go @@ -10,6 +10,7 @@ package syscall import ( + "io" "sync" ) @@ -252,15 +253,15 @@ func (f *naclFile) seek(off int64, whence int) (int64, error) { func (f *naclFile) prw(b []byte, offset int64, rw func([]byte) (int, error)) (int, error) { // NaCl has no pread; simulate with seek and hope for no races. - old, err := f.seek(0, 1) + old, err := f.seek(0, io.SeekCurrent) if err != nil { return 0, err } - if _, err := f.seek(offset, 0); err != nil { + if _, err := f.seek(offset, io.SeekStart); err != nil { return 0, err } n, err := rw(b) - f.seek(old, 0) + f.seek(old, io.SeekStart) return n, err } diff --git a/src/syscall/fs_nacl.go b/src/syscall/fs_nacl.go index 4019fad1a5d1e6..cbd9539c92cab3 100644 --- a/src/syscall/fs_nacl.go +++ b/src/syscall/fs_nacl.go @@ -15,6 +15,7 @@ package syscall import ( + "io" "sync" "unsafe" ) @@ -367,9 +368,9 @@ func (f *fsysFile) seek(offset int64, whence int) (int64, error) { f.fsys.mu.Lock() defer f.fsys.mu.Unlock() switch whence { - case 1: + case io.SeekCurrent: offset += f.offset - case 2: + case io.SeekEnd: offset += f.inode.Size } if offset < 0 { diff --git a/src/syscall/lsf_linux.go b/src/syscall/lsf_linux.go index 4a6aa2d6eb5329..b89239eba8a457 100644 --- a/src/syscall/lsf_linux.go +++ b/src/syscall/lsf_linux.go @@ -10,14 +10,17 @@ import ( "unsafe" ) +// Deprecated: Use golang.org/x/net/bpf instead. func LsfStmt(code, k int) *SockFilter { return &SockFilter{Code: uint16(code), K: uint32(k)} } +// Deprecated: Use golang.org/x/net/bpf instead. func LsfJump(code, k, jt, jf int) *SockFilter { return &SockFilter{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} } +// Deprecated: Use golang.org/x/net/bpf instead. func LsfSocket(ifindex, proto int) (int, error) { var lsall SockaddrLinklayer s, e := Socket(AF_PACKET, SOCK_RAW, proto) @@ -41,6 +44,7 @@ type iflags struct { flags uint16 } +// Deprecated: Use golang.org/x/net/bpf instead. func SetLsfPromisc(name string, m bool) error { s, e := Socket(AF_INET, SOCK_DGRAM, 0) if e != nil { @@ -65,6 +69,7 @@ func SetLsfPromisc(name string, m bool) error { return nil } +// Deprecated: Use golang.org/x/net/bpf instead. func AttachLsf(fd int, i []SockFilter) error { var p SockFprog p.Len = uint16(len(i)) @@ -72,6 +77,7 @@ func AttachLsf(fd int, i []SockFilter) error { return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, unsafe.Pointer(&p), unsafe.Sizeof(p)) } +// Deprecated: Use golang.org/x/net/bpf instead. func DetachLsf(fd int) error { var dummy int return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, unsafe.Pointer(&dummy), unsafe.Sizeof(dummy)) diff --git a/src/syscall/mksyscall_windows.go b/src/syscall/mksyscall_windows.go index a6cef6fca79e93..1e0d9401e746f4 100644 --- a/src/syscall/mksyscall_windows.go +++ b/src/syscall/mksyscall_windows.go @@ -57,6 +57,8 @@ import ( "io/ioutil" "log" "os" + "path/filepath" + "runtime" "sort" "strconv" "strings" @@ -66,8 +68,7 @@ import ( var ( filename = flag.String("output", "", "output file name (standard output if omitted)") printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall") - systemDLL = flag.Bool("systemdll", false, "whether all DLLs should be loaded from the Windows system directory") - sysRepo = flag.Bool("xsys", false, "whether this code is for the x/sys subrepo") + systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory") ) func trim(s string) string { @@ -596,14 +597,20 @@ func (f *Fn) HelperName() string { // Source files and functions. type Source struct { - Funcs []*Fn - Files []string - Imports []string + Funcs []*Fn + Files []string + StdLibImports []string + ExternalImports []string } func (src *Source) Import(pkg string) { - src.Imports = append(src.Imports, pkg) - sort.Strings(src.Imports) + src.StdLibImports = append(src.StdLibImports, pkg) + sort.Strings(src.StdLibImports) +} + +func (src *Source) ExternalImport(pkg string) { + src.ExternalImports = append(src.ExternalImports, pkg) + sort.Strings(src.ExternalImports) } // ParseFiles parses files listed in fs and extracts all syscall @@ -613,12 +620,10 @@ func ParseFiles(fs []string) (*Source, error) { src := &Source{ Funcs: make([]*Fn, 0), Files: make([]string, 0), - Imports: []string{ + StdLibImports: []string{ "unsafe", }, - } - if *systemDLL { - src.Import("internal/syscall/windows/sysdll") + ExternalImports: make([]string, 0), } for _, file := range fs { if err := src.ParseFile(file); err != nil { @@ -689,10 +694,52 @@ func (src *Source) ParseFile(path string) error { return nil } +// IsStdRepo returns true if src is part of standard library. +func (src *Source) IsStdRepo() (bool, error) { + if len(src.Files) == 0 { + return false, errors.New("no input files provided") + } + abspath, err := filepath.Abs(src.Files[0]) + if err != nil { + return false, err + } + goroot := runtime.GOROOT() + if runtime.GOOS == "windows" { + abspath = strings.ToLower(abspath) + goroot = strings.ToLower(goroot) + } + return strings.HasPrefix(abspath, goroot), nil +} + // Generate output source file from a source set src. func (src *Source) Generate(w io.Writer) error { - if *sysRepo && packageName != "windows" { - src.Import("golang.org/x/sys/windows") + const ( + pkgStd = iota // any package in std library + pkgXSysWindows // x/sys/windows package + pkgOther + ) + isStdRepo, err := src.IsStdRepo() + if err != nil { + return err + } + var pkgtype int + switch { + case isStdRepo: + pkgtype = pkgStd + case packageName == "windows": + // TODO: this needs better logic than just using package name + pkgtype = pkgXSysWindows + default: + pkgtype = pkgOther + } + if *systemDLL { + switch pkgtype { + case pkgStd: + src.Import("internal/syscall/windows/sysdll") + case pkgXSysWindows: + default: + src.ExternalImport("golang.org/x/sys/windows") + } } if packageName != "syscall" { src.Import("syscall") @@ -702,22 +749,21 @@ func (src *Source) Generate(w io.Writer) error { "syscalldot": syscalldot, "newlazydll": func(dll string) string { arg := "\"" + dll + ".dll\"" - if *systemDLL { - arg = "sysdll.Add(" + arg + ")" - } - if *sysRepo { - if packageName == "windows" { - return "NewLazySystemDLL(" + arg + ")" - } else { - return "windows.NewLazySystemDLL(" + arg + ")" - } - } else { + if !*systemDLL { return syscalldot() + "NewLazyDLL(" + arg + ")" } + switch pkgtype { + case pkgStd: + return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))" + case pkgXSysWindows: + return "NewLazySystemDLL(" + arg + ")" + default: + return "windows.NewLazySystemDLL(" + arg + ")" + } }, } t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate)) - err := t.Execute(w, src) + err = t.Execute(w, src) if err != nil { return errors.New("Failed to execute template: " + err.Error()) } @@ -770,7 +816,10 @@ const srcTemplate = ` package {{packagename}} import ( -{{range .Imports}}"{{.}}" +{{range .StdLibImports}}"{{.}}" +{{end}} + +{{range .ExternalImports}}"{{.}}" {{end}} ) diff --git a/src/syscall/route_bsd.go b/src/syscall/route_bsd.go index fe8259b221e27f..b364eeaba5dc05 100644 --- a/src/syscall/route_bsd.go +++ b/src/syscall/route_bsd.go @@ -176,6 +176,8 @@ func parseNetworkLayerAddr(b []byte, family byte) (Sockaddr, error) { // RouteRIB returns routing information base, as known as RIB, // which consists of network facility information, states and // parameters. +// +// Deprecated: Use golang.org/x/net/route instead. func RouteRIB(facility, param int) ([]byte, error) { mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)} // Find size. @@ -194,6 +196,8 @@ func RouteRIB(facility, param int) ([]byte, error) { } // RoutingMessage represents a routing message. +// +// Deprecated: Use golang.org/x/net/route instead. type RoutingMessage interface { sockaddr() ([]Sockaddr, error) } @@ -208,6 +212,8 @@ type anyMessage struct { // RouteMessage represents a routing message containing routing // entries. +// +// Deprecated: Use golang.org/x/net/route instead. type RouteMessage struct { Header RtMsghdr Data []byte @@ -252,6 +258,8 @@ func (m *RouteMessage) sockaddr() ([]Sockaddr, error) { // InterfaceMessage represents a routing message containing // network interface entries. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceMessage struct { Header IfMsghdr Data []byte @@ -272,6 +280,8 @@ func (m *InterfaceMessage) sockaddr() ([]Sockaddr, error) { // InterfaceAddrMessage represents a routing message containing // network interface address entries. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceAddrMessage struct { Header IfaMsghdr Data []byte @@ -316,6 +326,8 @@ func (m *InterfaceAddrMessage) sockaddr() ([]Sockaddr, error) { // ParseRoutingMessage parses b as routing messages and returns the // slice containing the RoutingMessage interfaces. +// +// Deprecated: Use golang.org/x/net/route instead. func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) { nmsgs, nskips := 0, 0 for len(b) >= anyMessageLen { @@ -341,6 +353,8 @@ func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) { // ParseRoutingSockaddr parses msg's payload as raw sockaddrs and // returns the slice containing the Sockaddr interfaces. +// +// Deprecated: Use golang.org/x/net/route instead. func ParseRoutingSockaddr(msg RoutingMessage) ([]Sockaddr, error) { sas, err := msg.sockaddr() if err != nil { diff --git a/src/syscall/route_bsd_test.go b/src/syscall/route_bsd_test.go deleted file mode 100644 index 74d11f9f0a578e..00000000000000 --- a/src/syscall/route_bsd_test.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd netbsd openbsd - -package syscall_test - -import ( - "fmt" - "net" - "os" - "syscall" - "testing" - "time" -) - -func TestRouteRIB(t *testing.T) { - for _, facility := range []int{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} { - for _, param := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} { - var err error - var b []byte - // The VM allocator wrapper functions can - // return ENOMEM easily. - for i := 0; i < 3; i++ { - b, err = syscall.RouteRIB(facility, param) - if err != nil { - time.Sleep(5 * time.Millisecond) - continue - } - break - } - if err != nil { - t.Error(facility, param, err) - continue - } - msgs, err := syscall.ParseRoutingMessage(b) - if err != nil { - t.Error(facility, param, err) - continue - } - var ipv4loopback, ipv6loopback bool - for _, m := range msgs { - flags, err := parseRoutingMessageHeader(m) - if err != nil { - t.Error(err) - continue - } - sas, err := parseRoutingSockaddrs(m) - if err != nil { - t.Error(err) - continue - } - if flags&(syscall.RTA_DST|syscall.RTA_IFA) != 0 { - sa := sas[syscall.RTAX_DST] - if sa == nil { - sa = sas[syscall.RTAX_IFA] - } - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - if net.IP(sa.Addr[:]).IsLoopback() { - ipv4loopback = true - } - case *syscall.SockaddrInet6: - if net.IP(sa.Addr[:]).IsLoopback() { - ipv6loopback = true - } - } - } - t.Log(facility, param, flags, sockaddrs(sas)) - } - if param == syscall.AF_UNSPEC && len(msgs) > 0 && !ipv4loopback && !ipv6loopback { - t.Errorf("no loopback facility found: ipv4/ipv6=%v/%v, %v", ipv4loopback, ipv6loopback, len(msgs)) - continue - } - } - } -} - -func TestRouteMonitor(t *testing.T) { - if testing.Short() || os.Getuid() != 0 { - t.Skip("must be root") - } - - s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) - if err != nil { - t.Fatal(err) - } - defer syscall.Close(s) - - tmo := time.After(30 * time.Second) - go func() { - b := make([]byte, os.Getpagesize()) - for { - n, err := syscall.Read(s, b) - if err != nil { - return - } - msgs, err := syscall.ParseRoutingMessage(b[:n]) - if err != nil { - t.Error(err) - return - } - for _, m := range msgs { - flags, err := parseRoutingMessageHeader(m) - if err != nil { - t.Error(err) - continue - } - sas, err := parseRoutingSockaddrs(m) - if err != nil { - t.Error(err) - continue - } - t.Log(flags, sockaddrs(sas)) - } - } - }() - <-tmo -} - -var parseInterfaceMessageTests = []*syscall.InterfaceMessage{ - // with link-layer address - { - Header: syscall.IfMsghdr{Version: syscall.RTM_VERSION, Addrs: syscall.RTA_IFP}, - Data: []uint8{ - 0x11, 0x12, 0x2, 0x0, 0x6, 0x3, 0x6, 0x0, - 0x77, 0x6d, 0x31, 0x01, 0x23, 0x45, 0xab, 0xcd, - 0xef, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }, - }, - // without link-layer address - { - Header: syscall.IfMsghdr{Version: syscall.RTM_VERSION, Addrs: syscall.RTA_IFP}, - Data: []uint8{ - 0xe, 0x12, 0x4, 0x0, 0xf5, 0x6, 0x0, 0x0, - 0x70, 0x66, 0x6c, 0x6f, 0x67, 0x30, 0x0, 0x0, - }, - }, - // no data - { - Header: syscall.IfMsghdr{Version: syscall.RTM_VERSION, Addrs: syscall.RTA_IFP}, - Data: []uint8{ - 0x8, 0xa, 0xb, 0xc, 0xd, 0x0, 0x0, 0x0, - }, - }, -} - -func TestParseInterfaceMessage(t *testing.T) { - for i, tt := range parseInterfaceMessageTests { - if _, err := syscall.ParseRoutingSockaddr(tt); err != nil { - t.Errorf("#%d: %v", i, err) - } - } -} - -type addrFamily byte - -func (f addrFamily) String() string { - switch f { - case syscall.AF_UNSPEC: - return "unspec" - case syscall.AF_LINK: - return "link" - case syscall.AF_INET: - return "inet4" - case syscall.AF_INET6: - return "inet6" - default: - return fmt.Sprintf("unknown %d", f) - } -} - -type addrFlags uint32 - -var addrFlagNames = [...]string{ - "dst", - "gateway", - "netmask", - "genmask", - "ifp", - "ifa", - "author", - "brd", - "mpls1,tag,src", // sockaddr_mpls=dragonfly,netbsd, sockaddr_in/in6=openbsd - "mpls2,srcmask", // sockaddr_mpls=dragonfly, sockaddr_in/in6=openbsd - "mpls3,label", // sockaddr_mpls=dragonfly, sockaddr_rtlabel=openbsd -} - -func (f addrFlags) String() string { - var s string - for i, name := range addrFlagNames { - if f&(1<" - } - return s -} - -type sockaddrs []syscall.Sockaddr - -func (sas sockaddrs) String() string { - var s string - for _, sa := range sas { - if sa == nil { - continue - } - if len(s) > 0 { - s += " " - } - switch sa := sa.(type) { - case *syscall.SockaddrDatalink: - s += fmt.Sprintf("[%v/%v/%v t/n/a/s=%v/%v/%v/%v]", sa.Len, addrFamily(sa.Family), sa.Index, sa.Type, sa.Nlen, sa.Alen, sa.Slen) - case *syscall.SockaddrInet4: - s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To4()) - case *syscall.SockaddrInet6: - s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To16()) - } - } - if s == "" { - return "" - } - return s -} - -func (sas sockaddrs) match(flags addrFlags) error { - var f addrFlags - family := syscall.AF_UNSPEC - for i := range sas { - if sas[i] != nil { - f |= 1 << uint(i) - } - switch sas[i].(type) { - case *syscall.SockaddrInet4: - if family == syscall.AF_UNSPEC { - family = syscall.AF_INET - } - if family != syscall.AF_INET { - return fmt.Errorf("got %v; want %v", sockaddrs(sas), family) - } - case *syscall.SockaddrInet6: - if family == syscall.AF_UNSPEC { - family = syscall.AF_INET6 - } - if family != syscall.AF_INET6 { - return fmt.Errorf("got %v; want %v", sockaddrs(sas), family) - } - } - } - if f != flags { - return fmt.Errorf("got %v; want %v", f, flags) - } - return nil -} diff --git a/src/syscall/route_darwin.go b/src/syscall/route_darwin.go index bb353b20111707..b0636ed07ceef0 100644 --- a/src/syscall/route_darwin.go +++ b/src/syscall/route_darwin.go @@ -26,6 +26,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { // InterfaceMulticastAddrMessage represents a routing message // containing network interface address entries. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceMulticastAddrMessage struct { Header IfmaMsghdr2 Data []byte diff --git a/src/syscall/route_dragonfly.go b/src/syscall/route_dragonfly.go index 78daf94deb4c41..b562400be8bdb7 100644 --- a/src/syscall/route_dragonfly.go +++ b/src/syscall/route_dragonfly.go @@ -31,6 +31,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { // InterfaceAnnounceMessage represents a routing message containing // network interface arrival and departure information. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } @@ -39,6 +41,8 @@ func (m *InterfaceAnnounceMessage) sockaddr() ([]Sockaddr, error) { return nil, // InterfaceMulticastAddrMessage represents a routing message // containing network interface address entries. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceMulticastAddrMessage struct { Header IfmaMsghdr Data []byte diff --git a/src/syscall/route_freebsd.go b/src/syscall/route_freebsd.go index fbfafbc1025521..2c2de7474a4415 100644 --- a/src/syscall/route_freebsd.go +++ b/src/syscall/route_freebsd.go @@ -53,6 +53,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { // InterfaceAnnounceMessage represents a routing message containing // network interface arrival and departure information. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } @@ -61,6 +63,8 @@ func (m *InterfaceAnnounceMessage) sockaddr() ([]Sockaddr, error) { return nil, // InterfaceMulticastAddrMessage represents a routing message // containing network interface address entries. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceMulticastAddrMessage struct { Header IfmaMsghdr Data []byte diff --git a/src/syscall/route_ifma_test.go b/src/syscall/route_ifma_test.go deleted file mode 100644 index af2b67dc24492e..00000000000000 --- a/src/syscall/route_ifma_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd - -package syscall_test - -import ( - "fmt" - "syscall" -) - -func parseRoutingMessageHeader(m syscall.RoutingMessage) (addrFlags, error) { - switch m := m.(type) { - case *syscall.RouteMessage: - errno := syscall.Errno(uintptr(m.Header.Errno)) - if errno != 0 { - return 0, fmt.Errorf("%T: %v, %#v", m, errno, m.Header) - } - return addrFlags(m.Header.Addrs), nil - case *syscall.InterfaceMessage: - return addrFlags(m.Header.Addrs), nil - case *syscall.InterfaceAddrMessage: - return addrFlags(m.Header.Addrs), nil - case *syscall.InterfaceMulticastAddrMessage: - return addrFlags(m.Header.Addrs), nil - default: - panic(fmt.Sprintf("unknown routing message type: %T", m)) - } -} - -func parseRoutingSockaddrs(m syscall.RoutingMessage) ([]syscall.Sockaddr, error) { - switch m := m.(type) { - case *syscall.RouteMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - case *syscall.InterfaceMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - case *syscall.InterfaceAddrMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - case *syscall.InterfaceMulticastAddrMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - default: - panic(fmt.Sprintf("unknown routing message type: %T", m)) - } -} diff --git a/src/syscall/route_netbsd.go b/src/syscall/route_netbsd.go index d21e3fa32c49c9..a10c8b65d9dbc2 100644 --- a/src/syscall/route_netbsd.go +++ b/src/syscall/route_netbsd.go @@ -28,6 +28,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { // InterfaceAnnounceMessage represents a routing message containing // network interface arrival and departure information. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } diff --git a/src/syscall/route_noifma_test.go b/src/syscall/route_noifma_test.go deleted file mode 100644 index 19d5d8ebbf277e..00000000000000 --- a/src/syscall/route_noifma_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build netbsd openbsd - -package syscall_test - -import ( - "fmt" - "syscall" -) - -func parseRoutingMessageHeader(m syscall.RoutingMessage) (addrFlags, error) { - switch m := m.(type) { - case *syscall.RouteMessage: - errno := syscall.Errno(uintptr(m.Header.Errno)) - if errno != 0 { - return 0, fmt.Errorf("%T: %v, %#v", m, errno, m.Header) - } - return addrFlags(m.Header.Addrs), nil - case *syscall.InterfaceMessage: - return addrFlags(m.Header.Addrs), nil - case *syscall.InterfaceAddrMessage: - return addrFlags(m.Header.Addrs), nil - default: - panic(fmt.Sprintf("unknown routing message type: %T", m)) - } -} - -func parseRoutingSockaddrs(m syscall.RoutingMessage) ([]syscall.Sockaddr, error) { - switch m := m.(type) { - case *syscall.RouteMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - case *syscall.InterfaceMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - case *syscall.InterfaceAddrMessage: - sas, err := syscall.ParseRoutingSockaddr(m) - if err != nil { - return nil, fmt.Errorf("%T: %v, %#v", m, err, m.Data) - } - if err = sockaddrs(sas).match(addrFlags(m.Header.Addrs)); err != nil { - return nil, err - } - return sas, nil - default: - panic(fmt.Sprintf("unknown routing message type: %T", m)) - } -} diff --git a/src/syscall/route_openbsd.go b/src/syscall/route_openbsd.go index 719396db53beb0..fe173adda8879e 100644 --- a/src/syscall/route_openbsd.go +++ b/src/syscall/route_openbsd.go @@ -28,6 +28,8 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { // InterfaceAnnounceMessage represents a routing message containing // network interface arrival and departure information. +// +// Deprecated: Use golang.org/x/net/route instead. type InterfaceAnnounceMessage struct { Header IfAnnounceMsghdr } diff --git a/src/syscall/syscall_plan9.go b/src/syscall/syscall_plan9.go index 796870825c98dc..b511867cda51d4 100644 --- a/src/syscall/syscall_plan9.go +++ b/src/syscall/syscall_plan9.go @@ -56,6 +56,7 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorSt func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +//go:nosplit func atoi(b []byte) (n uint) { n = 0 for i := 0; i < len(b); i++ { diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go index c7b4560b76bc5e..80544f331923d6 100644 --- a/src/syscall/syscall_unix_test.go +++ b/src/syscall/syscall_unix_test.go @@ -10,6 +10,7 @@ import ( "flag" "fmt" "internal/testenv" + "io" "io/ioutil" "net" "os" @@ -244,7 +245,7 @@ func passFDChild() { } f.Write([]byte("Hello from child process!\n")) - f.Seek(0, 0) + f.Seek(0, io.SeekStart) rights := syscall.UnixRights(int(f.Fd())) dummyByte := []byte("x") @@ -344,7 +345,7 @@ func TestRlimit(t *testing.T) { } func TestSeekFailure(t *testing.T) { - _, err := syscall.Seek(-1, 0, 0) + _, err := syscall.Seek(-1, 0, io.SeekStart) if err == nil { t.Fatalf("Seek(-1, 0, 0) did not fail") } diff --git a/src/syscall/zsyscall_darwin_386.go b/src/syscall/zsyscall_darwin_386.go index 23e7b5e420bac3..9c3ba5a81a0515 100644 --- a/src/syscall/zsyscall_darwin_386.go +++ b/src/syscall/zsyscall_darwin_386.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go index 6e63d9a0743fa9..12f4782296c5e5 100644 --- a/src/syscall/zsyscall_darwin_amd64.go +++ b/src/syscall/zsyscall_darwin_amd64.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_darwin_arm.go b/src/syscall/zsyscall_darwin_arm.go index f996a508f020a1..ab5b4a97bab7ad 100644 --- a/src/syscall/zsyscall_darwin_arm.go +++ b/src/syscall/zsyscall_darwin_arm.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_dragonfly_amd64.go b/src/syscall/zsyscall_dragonfly_amd64.go index 88e09d3a14d211..85d27777ba7483 100644 --- a/src/syscall/zsyscall_dragonfly_amd64.go +++ b/src/syscall/zsyscall_dragonfly_amd64.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_freebsd_386.go b/src/syscall/zsyscall_freebsd_386.go index 30f29e52a9f6fe..b9ed271486b8ca 100644 --- a/src/syscall/zsyscall_freebsd_386.go +++ b/src/syscall/zsyscall_freebsd_386.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_freebsd_amd64.go b/src/syscall/zsyscall_freebsd_amd64.go index 93059d1b5bbb10..12d1db0c86186e 100644 --- a/src/syscall/zsyscall_freebsd_amd64.go +++ b/src/syscall/zsyscall_freebsd_amd64.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_freebsd_arm.go b/src/syscall/zsyscall_freebsd_arm.go index 84096b07a59bff..78b7c07a0fa591 100644 --- a/src/syscall/zsyscall_freebsd_arm.go +++ b/src/syscall/zsyscall_freebsd_arm.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_netbsd_386.go b/src/syscall/zsyscall_netbsd_386.go index e24c3b71cdad79..61b52cd165fea9 100644 --- a/src/syscall/zsyscall_netbsd_386.go +++ b/src/syscall/zsyscall_netbsd_386.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_netbsd_amd64.go b/src/syscall/zsyscall_netbsd_amd64.go index 7aa75ab12df43d..52987ba9023702 100644 --- a/src/syscall/zsyscall_netbsd_amd64.go +++ b/src/syscall/zsyscall_netbsd_amd64.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_netbsd_arm.go b/src/syscall/zsyscall_netbsd_arm.go index 21f482b40fdf0e..5c59a0ded161ac 100644 --- a/src/syscall/zsyscall_netbsd_arm.go +++ b/src/syscall/zsyscall_netbsd_arm.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_openbsd_386.go b/src/syscall/zsyscall_openbsd_386.go index df7df1e7e457ee..37bbd85de5c560 100644 --- a/src/syscall/zsyscall_openbsd_386.go +++ b/src/syscall/zsyscall_openbsd_386.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/syscall/zsyscall_openbsd_amd64.go b/src/syscall/zsyscall_openbsd_amd64.go index 1d640700f7d6a8..0d831df1f67eb1 100644 --- a/src/syscall/zsyscall_openbsd_amd64.go +++ b/src/syscall/zsyscall_openbsd_amd64.go @@ -217,6 +217,7 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) _p0 = unsafe.Pointer(&_zero) } _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + use(_p0) if e1 != 0 { err = errnoErr(e1) } diff --git a/src/testing/match_test.go b/src/testing/match_test.go index d19036c72d1001..8c1c5f4452c5c0 100644 --- a/src/testing/match_test.go +++ b/src/testing/match_test.go @@ -135,8 +135,8 @@ func TestMatcher(t *T) { parent.level = 1 } if n, ok := m.fullName(parent, tc.sub); ok != tc.ok { - t.Errorf("pattern: %q, parent: %q, sub %q: got %v; want %v", - tc.pattern, tc.parent, tc.sub, ok, tc.ok, n) + t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v; want ok %v", + tc.pattern, tc.parent, tc.sub, n, ok, tc.ok) } } } diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go index 4bc8e3fc2e1479..798d41aa7d176b 100644 --- a/src/testing/quick/quick.go +++ b/src/testing/quick/quick.go @@ -14,7 +14,7 @@ import ( "strings" ) -var defaultMaxCount = flag.Int("quickchecks", 100, "The default number of iterations for each check") +var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check") // A Generator can generate random values of its own type. type Generator interface { @@ -98,22 +98,18 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, case reflect.Uintptr: v.SetUint(uint64(randInt64(rand))) case reflect.Map: - if generateNilValue(rand) { - v.Set(reflect.Zero(concrete)) // Generate nil map. - } else { - numElems := rand.Intn(size) - v.Set(reflect.MakeMap(concrete)) - for i := 0; i < numElems; i++ { - key, ok1 := sizedValue(concrete.Key(), rand, size) - value, ok2 := sizedValue(concrete.Elem(), rand, size) - if !ok1 || !ok2 { - return reflect.Value{}, false - } - v.SetMapIndex(key, value) + numElems := rand.Intn(size) + v.Set(reflect.MakeMap(concrete)) + for i := 0; i < numElems; i++ { + key, ok1 := sizedValue(concrete.Key(), rand, size) + value, ok2 := sizedValue(concrete.Elem(), rand, size) + if !ok1 || !ok2 { + return reflect.Value{}, false } + v.SetMapIndex(key, value) } case reflect.Ptr: - if generateNilValue(rand) { + if rand.Intn(size) == 0 { v.Set(reflect.Zero(concrete)) // Generate nil pointer. } else { elem, ok := sizedValue(concrete.Elem(), rand, size) @@ -124,20 +120,15 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, v.Elem().Set(elem) } case reflect.Slice: - if generateNilValue(rand) { - v.Set(reflect.Zero(concrete)) // Generate nil slice. - } else { - slCap := rand.Intn(size) - slLen := rand.Intn(slCap + 1) - sizeLeft := size - slCap - v.Set(reflect.MakeSlice(concrete, slLen, slCap)) - for i := 0; i < slLen; i++ { - elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft) - if !ok { - return reflect.Value{}, false - } - v.Index(i).Set(elem) + numElems := rand.Intn(size) + sizeLeft := size - numElems + v.Set(reflect.MakeSlice(concrete, numElems, numElems)) + for i := 0; i < numElems; i++ { + elem, ok := sizedValue(concrete.Elem(), rand, sizeLeft) + if !ok { + return reflect.Value{}, false } + v.Index(i).Set(elem) } case reflect.Array: for i := 0; i < v.Len(); i++ { @@ -385,5 +376,3 @@ func toString(interfaces []interface{}) string { } return strings.Join(s, ", ") } - -func generateNilValue(r *rand.Rand) bool { return r.Intn(20) == 0 } diff --git a/src/testing/quick/quick_test.go b/src/testing/quick/quick_test.go index 018ece2a5289ed..fe443592f87bed 100644 --- a/src/testing/quick/quick_test.go +++ b/src/testing/quick/quick_test.go @@ -290,3 +290,20 @@ func TestMutuallyRecursive(t *testing.T) { f := func(a A) bool { return true } Check(f, nil) } + +// Some serialization formats (e.g. encoding/pem) cannot distinguish +// between a nil and an empty map or slice, so avoid generating the +// zero value for these. +func TestNonZeroSliceAndMap(t *testing.T) { + type Q struct { + M map[int]int + S []int + } + f := func(q Q) bool { + return q.M != nil && q.S != nil + } + err := Check(f, nil) + if err != nil { + t.Fatal(err) + } +} diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 2804550737429b..2a24aaacfd72a0 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -307,6 +307,27 @@ func TestTRun(t *T) { f: func(t *T) { t.Skip() }, + }, { + desc: "panic on goroutine fail after test exit", + ok: false, + maxPar: 4, + f: func(t *T) { + ch := make(chan bool) + t.Run("", func(t *T) { + go func() { + <-ch + defer func() { + if r := recover(); r == nil { + realTest.Errorf("expected panic") + } + ch <- true + }() + t.Errorf("failed after success") + }() + }) + ch <- true + <-ch + }, }} for _, tc := range testCases { ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) diff --git a/src/testing/testing.go b/src/testing/testing.go index 3a7a135a3c6842..657a7b731fabe6 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -118,6 +118,61 @@ // example function, at least one other function, type, variable, or constant // declaration, and no test or benchmark functions. // +// Subtests and Sub-benchmarks +// +// The Run methods of T and B allow defining subtests and sub-benchmarks, +// without having to define separate functions for each. This enables uses +// like table-driven benchmarks and creating hierarchical tests. +// It also provides a way to share common setup and tear-down code: +// +// func TestFoo(t *testing.T) { +// // +// t.Run("A=1", func(t *testing.T) { ... }) +// t.Run("A=2", func(t *testing.T) { ... }) +// t.Run("B=1", func(t *testing.T) { ... }) +// // +// } +// +// Each subtest and sub-benchmark has a unique name: the combination of the name +// of the top-level test and the sequence of names passed to Run, separated by +// slashes, with an optional trailing sequence number for disambiguation. +// +// The argument to the -run and -bench command-line flags is a slash-separated +// list of regular expressions that match each name element in turn. +// For example: +// +// go test -run Foo # Run top-level tests matching "Foo". +// go test -run Foo/A= # Run subtests of Foo matching "A=". +// go test -run /A=1 # Run all subtests of a top-level test matching "A=1". +// +// Subtests can also be used to control parallelism. A parent test will only +// complete once all of its subtests complete. In this example, all tests are +// run in parallel with each other, and only with each other, regardless of +// other top-level tests that may be defined: +// +// func TestGroupedParallel(t *testing.T) { +// for _, tc := range tests { +// tc := tc // capture range variable +// t.Run(tc.Name, func(t *testing.T) { +// t.Parallel() +// ... +// }) +// } +// } +// +// Run does not return until parallel subtests have completed, providing a way +// to clean up after a group of parallel tests: +// +// func TestTeardownParallel(t *testing.T) { +// // This Run will not return until the parallel tests finish. +// t.Run("group", func(t *testing.T) { +// t.Run("Test1", parallelTest1) +// t.Run("Test2", parallelTest2) +// t.Run("Test3", parallelTest3) +// }) +// // +// } +// // Main // // It is sometimes necessary for a test program to do extra setup or teardown @@ -196,13 +251,14 @@ var ( // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { - mu sync.RWMutex // guards output and failed + mu sync.RWMutex // guards output, failed, and done. output []byte // Output generated by test or benchmark. w io.Writer // For flushToParent. chatty bool // A copy of the chatty flag. failed bool // Test or benchmark has failed. skipped bool // Test of benchmark has been skipped. - finished bool + finished bool // Test function has completed. + done bool // Test is finished and all subtests have completed. parent *common level int // Nesting depth of test or benchmark. @@ -351,6 +407,10 @@ func (c *common) Fail() { } c.mu.Lock() defer c.mu.Unlock() + // c.done needs to be locked to synchronize checks to c.done in parent tests. + if c.done { + panic("Fail in goroutine after " + c.name + " has completed") + } c.failed = true } @@ -540,6 +600,9 @@ func tRunner(t *T, fn func(t *T)) { } t.report() // Report after all subtests have finished. + // Do not lock t.done to allow race detector to detect race in case + // the user does not appropriately synchronizes a goroutine. + t.done = true t.signal <- true }() diff --git a/src/text/scanner/example_test.go b/src/text/scanner/example_test.go index 101145948fac43..1d5d34a0152627 100644 --- a/src/text/scanner/example_test.go +++ b/src/text/scanner/example_test.go @@ -17,6 +17,7 @@ func Example() { someParsable = text }` var s scanner.Scanner + s.Filename = "example" s.Init(strings.NewReader(src)) var tok rune for tok != scanner.EOF { @@ -25,14 +26,14 @@ func Example() { } // Output: - // At position 3:4 : if - // At position 3:6 : a - // At position 3:8 : > - // At position 3:11 : 10 - // At position 3:13 : { - // At position 4:15 : someParsable - // At position 4:17 : = - // At position 4:22 : text - // At position 5:3 : } - // At position 5:3 : + // At position example:3:4 : if + // At position example:3:6 : a + // At position example:3:8 : > + // At position example:3:11 : 10 + // At position example:3:13 : { + // At position example:4:15 : someParsable + // At position example:4:17 : = + // At position example:4:22 : text + // At position example:5:3 : } + // At position example:5:3 : } diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go index a3da1fdabf4455..e085f8a7d956bb 100644 --- a/src/text/scanner/scanner.go +++ b/src/text/scanner/scanner.go @@ -37,14 +37,11 @@ func (pos *Position) IsValid() bool { return pos.Line > 0 } func (pos Position) String() string { s := pos.Filename - if pos.IsValid() { - if s != "" { - s += ":" - } - s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) - } if s == "" { - s = "???" + s = "" + } + if pos.IsValid() { + s += fmt.Sprintf(":%d:%d", pos.Line, pos.Column) } return s } @@ -333,7 +330,7 @@ func (s *Scanner) error(msg string) { if !pos.IsValid() { pos = s.Pos() } - fmt.Fprintf(os.Stderr, "text/scanner: %s: %s\n", pos, msg) + fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) } func (s *Scanner) isIdentRune(ch rune, i int) bool { diff --git a/src/text/scanner/scanner_test.go b/src/text/scanner/scanner_test.go index 798bed7e92aa0a..3e92d659ca029e 100644 --- a/src/text/scanner/scanner_test.go +++ b/src/text/scanner/scanner_test.go @@ -451,37 +451,37 @@ func testError(t *testing.T, src, pos, msg string, tok rune) { } func TestError(t *testing.T) { - testError(t, "\x00", "1:1", "illegal character NUL", 0) - testError(t, "\x80", "1:1", "illegal UTF-8 encoding", utf8.RuneError) - testError(t, "\xff", "1:1", "illegal UTF-8 encoding", utf8.RuneError) - - testError(t, "a\x00", "1:2", "illegal character NUL", Ident) - testError(t, "ab\x80", "1:3", "illegal UTF-8 encoding", Ident) - testError(t, "abc\xff", "1:4", "illegal UTF-8 encoding", Ident) - - testError(t, `"a`+"\x00", "1:3", "illegal character NUL", String) - testError(t, `"ab`+"\x80", "1:4", "illegal UTF-8 encoding", String) - testError(t, `"abc`+"\xff", "1:5", "illegal UTF-8 encoding", String) - - testError(t, "`a"+"\x00", "1:3", "illegal character NUL", String) - testError(t, "`ab"+"\x80", "1:4", "illegal UTF-8 encoding", String) - testError(t, "`abc"+"\xff", "1:5", "illegal UTF-8 encoding", String) - - testError(t, `'\"'`, "1:3", "illegal char escape", Char) - testError(t, `"\'"`, "1:3", "illegal char escape", String) - - testError(t, `01238`, "1:6", "illegal octal number", Int) - testError(t, `01238123`, "1:9", "illegal octal number", Int) - testError(t, `0x`, "1:3", "illegal hexadecimal number", Int) - testError(t, `0xg`, "1:3", "illegal hexadecimal number", Int) - testError(t, `'aa'`, "1:4", "illegal char literal", Char) - - testError(t, `'`, "1:2", "literal not terminated", Char) - testError(t, `'`+"\n", "1:2", "literal not terminated", Char) - testError(t, `"abc`, "1:5", "literal not terminated", String) - testError(t, `"abc`+"\n", "1:5", "literal not terminated", String) - testError(t, "`abc\n", "2:1", "literal not terminated", String) - testError(t, `/*/`, "1:4", "comment not terminated", EOF) + testError(t, "\x00", ":1:1", "illegal character NUL", 0) + testError(t, "\x80", ":1:1", "illegal UTF-8 encoding", utf8.RuneError) + testError(t, "\xff", ":1:1", "illegal UTF-8 encoding", utf8.RuneError) + + testError(t, "a\x00", ":1:2", "illegal character NUL", Ident) + testError(t, "ab\x80", ":1:3", "illegal UTF-8 encoding", Ident) + testError(t, "abc\xff", ":1:4", "illegal UTF-8 encoding", Ident) + + testError(t, `"a`+"\x00", ":1:3", "illegal character NUL", String) + testError(t, `"ab`+"\x80", ":1:4", "illegal UTF-8 encoding", String) + testError(t, `"abc`+"\xff", ":1:5", "illegal UTF-8 encoding", String) + + testError(t, "`a"+"\x00", ":1:3", "illegal character NUL", String) + testError(t, "`ab"+"\x80", ":1:4", "illegal UTF-8 encoding", String) + testError(t, "`abc"+"\xff", ":1:5", "illegal UTF-8 encoding", String) + + testError(t, `'\"'`, ":1:3", "illegal char escape", Char) + testError(t, `"\'"`, ":1:3", "illegal char escape", String) + + testError(t, `01238`, ":1:6", "illegal octal number", Int) + testError(t, `01238123`, ":1:9", "illegal octal number", Int) + testError(t, `0x`, ":1:3", "illegal hexadecimal number", Int) + testError(t, `0xg`, ":1:3", "illegal hexadecimal number", Int) + testError(t, `'aa'`, ":1:4", "illegal char literal", Char) + + testError(t, `'`, ":1:2", "literal not terminated", Char) + testError(t, `'`+"\n", ":1:2", "literal not terminated", Char) + testError(t, `"abc`, ":1:5", "literal not terminated", String) + testError(t, `"abc`+"\n", ":1:5", "literal not terminated", String) + testError(t, "`abc\n", ":2:1", "literal not terminated", String) + testError(t, `/*/`, ":1:4", "comment not terminated", EOF) } // An errReader returns (0, err) where err is not io.EOF. diff --git a/src/text/template/doc.go b/src/text/template/doc.go index df8c95f8c8949e..48e9aa7395cec9 100644 --- a/src/text/template/doc.go +++ b/src/text/template/doc.go @@ -220,7 +220,7 @@ value (argument) or a function or method call, possibly with multiple arguments: Functions and function names are described below. A pipeline may be "chained" by separating a sequence of commands with pipeline -characters '|'. In a chained pipeline, the result of the each command is +characters '|'. In a chained pipeline, the result of each command is passed as the last argument of the following command. The output of the final command in the pipeline is the value of the pipeline. diff --git a/src/text/template/exec.go b/src/text/template/exec.go index 22881c685279f6..8e5ad93ca6b5b8 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -15,14 +15,21 @@ import ( "text/template/parse" ) +// maxExecDepth specifies the maximum stack depth of templates within +// templates. This limit is only practically reached by accidentally +// recursive template invocations. This limit allows us to return +// an error instead of triggering a stack overflow. +const maxExecDepth = 100000 + // state represents the state of an execution. It's not part of the // template so that multiple executions of the same template // can execute in parallel. type state struct { - tmpl *Template - wr io.Writer - node parse.Node // current node, for errors - vars []variable // push-down stack of variable values. + tmpl *Template + wr io.Writer + node parse.Node // current node, for errors + vars []variable // push-down stack of variable values. + depth int // the height of the stack of executing templates. } // variable holds the dynamic value of a variable such as $, $x etc. @@ -363,9 +370,13 @@ func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { if tmpl == nil { s.errorf("template %q not defined", t.Name) } + if s.depth == maxExecDepth { + s.errorf("exceeded maximum template depth (%v)", maxExecDepth) + } // Variables declared by the pipeline persist. dot = s.evalPipeline(dot, t.Pipe) newState := *s + newState.depth++ newState.tmpl = tmpl // No dynamic scoping: template invocations inherit no variables. newState.vars = []variable{{"$", dot}} diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index bc2aa683ec9a06..3ef065edcfdebe 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -1297,3 +1297,16 @@ func TestMissingFieldOnNil(t *testing.T) { t.Errorf("got error %q, want %q", got, want) } } + +func TestMaxExecDepth(t *testing.T) { + tmpl := Must(New("tmpl").Parse(`{{template "tmpl" .}}`)) + err := tmpl.Execute(ioutil.Discard, nil) + got := "" + if err != nil { + got = err.Error() + } + const want = "exceeded maximum template depth" + if !strings.Contains(got, want) { + t.Errorf("got error %q; want %q", got, want) + } +} diff --git a/src/time/sleep.go b/src/time/sleep.go index c348366604e6db..7661f7e54fc130 100644 --- a/src/time/sleep.go +++ b/src/time/sleep.go @@ -106,6 +106,9 @@ func sendTime(c interface{}, seq uintptr) { // After waits for the duration to elapse and then sends the current time // on the returned channel. // It is equivalent to NewTimer(d).C. +// The underlying Timer is not recovered by the garbage collector +// until the timer fires. If efficiency is a concern, use NewTimer +// instead and call Timer.Stop if the timer is no longer needed. func After(d Duration) <-chan Time { return NewTimer(d).C } diff --git a/src/time/sys_plan9.go b/src/time/sys_plan9.go index 8484729448eb96..11365a791f7dfc 100644 --- a/src/time/sys_plan9.go +++ b/src/time/sys_plan9.go @@ -55,9 +55,9 @@ func closefd(fd uintptr) { } func preadn(fd uintptr, buf []byte, off int) error { - whence := 0 + whence := seekStart if off < 0 { - whence = 2 + whence = seekEnd } if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil { return err diff --git a/src/time/sys_unix.go b/src/time/sys_unix.go index e592415daa5161..91d54c9ffd0a9e 100644 --- a/src/time/sys_unix.go +++ b/src/time/sys_unix.go @@ -55,9 +55,9 @@ func closefd(fd uintptr) { } func preadn(fd uintptr, buf []byte, off int) error { - whence := 0 + whence := seekStart if off < 0 { - whence = 2 + whence = seekEnd } if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil { return err diff --git a/src/time/sys_windows.go b/src/time/sys_windows.go index de63b4bf4bb87b..a4a068f78494c1 100644 --- a/src/time/sys_windows.go +++ b/src/time/sys_windows.go @@ -52,9 +52,9 @@ func closefd(fd uintptr) { } func preadn(fd uintptr, buf []byte, off int) error { - whence := 0 + whence := seekStart if off < 0 { - whence = 2 + whence = seekEnd } if _, err := syscall.Seek(syscall.Handle(fd), int64(off), whence); err != nil { return err diff --git a/src/time/zoneinfo_abbrs_windows.go b/src/time/zoneinfo_abbrs_windows.go index 51a1a2f66d87e3..344a891d1a4eb1 100644 --- a/src/time/zoneinfo_abbrs_windows.go +++ b/src/time/zoneinfo_abbrs_windows.go @@ -22,9 +22,10 @@ var abbrs = map[string]abbr{ "Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek "Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage "Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion - "Bahia Standard Time": {"BRT", "BRST"}, // America/Bahia + "Bahia Standard Time": {"BRT", "BRT"}, // America/Bahia "SA Pacific Standard Time": {"COT", "COT"}, // America/Bogota "Argentina Standard Time": {"ART", "ART"}, // America/Buenos_Aires + "Eastern Standard Time (Mexico)": {"EST", "EST"}, // America/Cancun "Venezuela Standard Time": {"VET", "VET"}, // America/Caracas "SA Eastern Standard Time": {"GFT", "GFT"}, // America/Cayenne "Central Standard Time": {"CST", "CDT"}, // America/Chicago @@ -38,21 +39,22 @@ var abbrs = map[string]abbr{ "SA Western Standard Time": {"BOT", "BOT"}, // America/La_Paz "Pacific Standard Time": {"PST", "PDT"}, // America/Los_Angeles "Central Standard Time (Mexico)": {"CST", "CDT"}, // America/Mexico_City - "Montevideo Standard Time": {"UYT", "UYST"}, // America/Montevideo + "Montevideo Standard Time": {"UYT", "UYT"}, // America/Montevideo "Eastern Standard Time": {"EST", "EDT"}, // America/New_York "US Mountain Standard Time": {"MST", "MST"}, // America/Phoenix "Canada Central Standard Time": {"CST", "CST"}, // America/Regina - "Pacific Standard Time (Mexico)": {"PST", "PDT"}, // America/Santa_Isabel "Pacific SA Standard Time": {"CLT", "CLST"}, // America/Santiago "E. South America Standard Time": {"BRT", "BRST"}, // America/Sao_Paulo "Newfoundland Standard Time": {"NST", "NDT"}, // America/St_Johns - "Central Asia Standard Time": {"ALMT", "ALMT"}, // Asia/Almaty + "Central Asia Standard Time": {"+06", "+06"}, // Asia/Almaty "Jordan Standard Time": {"EET", "EEST"}, // Asia/Amman "Arabic Standard Time": {"AST", "AST"}, // Asia/Baghdad - "Azerbaijan Standard Time": {"AZT", "AZST"}, // Asia/Baku + "Azerbaijan Standard Time": {"AZT", "AZT"}, // Asia/Baku "SE Asia Standard Time": {"ICT", "ICT"}, // Asia/Bangkok + "Altai Standard Time": {"+06", "+07"}, // Asia/Barnaul "Middle East Standard Time": {"EET", "EEST"}, // Asia/Beirut "India Standard Time": {"IST", "IST"}, // Asia/Calcutta + "Transbaikal Standard Time": {"IRKT", "YAKT"}, // Asia/Chita "Sri Lanka Standard Time": {"IST", "IST"}, // Asia/Colombo "Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus "Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka @@ -60,22 +62,26 @@ var abbrs = map[string]abbr{ "North Asia East Standard Time": {"IRKT", "IRKT"}, // Asia/Irkutsk "Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem "Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul + "Russia Time Zone 11": {"PETT", "PETT"}, // Asia/Kamchatka "Pakistan Standard Time": {"PKT", "PKT"}, // Asia/Karachi "Nepal Standard Time": {"NPT", "NPT"}, // Asia/Katmandu "North Asia Standard Time": {"KRAT", "KRAT"}, // Asia/Krasnoyarsk "Magadan Standard Time": {"MAGT", "MAGT"}, // Asia/Magadan "N. Central Asia Standard Time": {"NOVT", "NOVT"}, // Asia/Novosibirsk + "North Korea Standard Time": {"KST", "KST"}, // Asia/Pyongyang "Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon "Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh + "Sakhalin Standard Time": {"SAKT", "SAKT"}, // Asia/Sakhalin "Korea Standard Time": {"KST", "KST"}, // Asia/Seoul "China Standard Time": {"CST", "CST"}, // Asia/Shanghai "Singapore Standard Time": {"SGT", "SGT"}, // Asia/Singapore + "Russia Time Zone 10": {"SRET", "SRET"}, // Asia/Srednekolymsk "Taipei Standard Time": {"CST", "CST"}, // Asia/Taipei "West Asia Standard Time": {"UZT", "UZT"}, // Asia/Tashkent "Georgian Standard Time": {"GET", "GET"}, // Asia/Tbilisi "Iran Standard Time": {"IRST", "IRDT"}, // Asia/Tehran "Tokyo Standard Time": {"JST", "JST"}, // Asia/Tokyo - "Ulaanbaatar Standard Time": {"ULAT", "ULAT"}, // Asia/Ulaanbaatar + "Ulaanbaatar Standard Time": {"ULAT", "ULAST"}, // Asia/Ulaanbaatar "Vladivostok Standard Time": {"VLAT", "VLAT"}, // Asia/Vladivostok "Yakutsk Standard Time": {"YAKT", "YAKT"}, // Asia/Yakutsk "Ekaterinburg Standard Time": {"YEKT", "YEKT"}, // Asia/Yekaterinburg @@ -83,31 +89,35 @@ var abbrs = map[string]abbr{ "Azores Standard Time": {"AZOT", "AZOST"}, // Atlantic/Azores "Cape Verde Standard Time": {"CVT", "CVT"}, // Atlantic/Cape_Verde "Greenwich Standard Time": {"GMT", "GMT"}, // Atlantic/Reykjavik - "Cen. Australia Standard Time": {"CST", "CST"}, // Australia/Adelaide - "E. Australia Standard Time": {"EST", "EST"}, // Australia/Brisbane - "AUS Central Standard Time": {"CST", "CST"}, // Australia/Darwin - "Tasmania Standard Time": {"EST", "EST"}, // Australia/Hobart - "W. Australia Standard Time": {"WST", "WST"}, // Australia/Perth - "AUS Eastern Standard Time": {"EST", "EST"}, // Australia/Sydney + "Cen. Australia Standard Time": {"ACST", "ACDT"}, // Australia/Adelaide + "E. Australia Standard Time": {"AEST", "AEST"}, // Australia/Brisbane + "AUS Central Standard Time": {"ACST", "ACST"}, // Australia/Darwin + "Tasmania Standard Time": {"AEST", "AEDT"}, // Australia/Hobart + "W. Australia Standard Time": {"AWST", "AWST"}, // Australia/Perth + "AUS Eastern Standard Time": {"AEST", "AEDT"}, // Australia/Sydney "UTC": {"GMT", "GMT"}, // Etc/GMT "UTC-11": {"GMT+11", "GMT+11"}, // Etc/GMT+11 "Dateline Standard Time": {"GMT+12", "GMT+12"}, // Etc/GMT+12 "UTC-02": {"GMT+2", "GMT+2"}, // Etc/GMT+2 "UTC+12": {"GMT-12", "GMT-12"}, // Etc/GMT-12 + "Astrakhan Standard Time": {"+03", "+04"}, // Europe/Astrakhan "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest + "E. Europe Standard Time": {"EET", "EEST"}, // Europe/Chisinau "Turkey Standard Time": {"EET", "EEST"}, // Europe/Istanbul - "Kaliningrad Standard Time": {"FET", "FET"}, // Europe/Kaliningrad + "Kaliningrad Standard Time": {"EET", "EET"}, // Europe/Kaliningrad "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev "GMT Standard Time": {"GMT", "BST"}, // Europe/London + "Belarus Standard Time": {"MSK", "MSK"}, // Europe/Minsk "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris + "Russia Time Zone 3": {"SAMT", "SAMT"}, // Europe/Samara "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw "Mauritius Standard Time": {"MUT", "MUT"}, // Indian/Mauritius - "Samoa Standard Time": {"WST", "WST"}, // Pacific/Apia + "Samoa Standard Time": {"WSST", "WSDT"}, // Pacific/Apia "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland - "Fiji Standard Time": {"FJT", "FJT"}, // Pacific/Fiji + "Fiji Standard Time": {"FJT", "FJST"}, // Pacific/Fiji "Central Pacific Standard Time": {"SBT", "SBT"}, // Pacific/Guadalcanal "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu "Line Islands Standard Time": {"LINT", "LINT"}, // Pacific/Kiritimati diff --git a/src/time/zoneinfo_read.go b/src/time/zoneinfo_read.go index 66777f6d736151..19cd40d8477297 100644 --- a/src/time/zoneinfo_read.go +++ b/src/time/zoneinfo_read.go @@ -11,6 +11,13 @@ package time import "errors" +// Copies of io.Seek* constants to avoid importing "io": +const ( + seekStart = 0 + seekCurrent = 1 + seekEnd = 2 +) + // Simple I/O interface to binary blob of data. type data struct { p []byte diff --git a/src/time/zoneinfo_test.go b/src/time/zoneinfo_test.go index 0b7584ab9c2ffd..4b50dc509002f0 100644 --- a/src/time/zoneinfo_test.go +++ b/src/time/zoneinfo_test.go @@ -61,3 +61,12 @@ func TestFirstZone(t *testing.T) { } } } + +func TestLocationNames(t *testing.T) { + if time.Local.String() != "Local" { + t.Errorf(`invalid Local location name: got %q want "Local"`, time.Local) + } + if time.UTC.String() != "UTC" { + t.Errorf(`invalid UTC location name: got %q want "UTC"`, time.UTC) + } +} diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go index c753119d5db765..a6546f54b86ac7 100644 --- a/src/time/zoneinfo_windows.go +++ b/src/time/zoneinfo_windows.go @@ -140,6 +140,8 @@ func pseudoUnix(year int, d *syscall.Systemtime) int64 { func initLocalFromTZI(i *syscall.Timezoneinformation) { l := &localLoc + l.name = "Local" + nzone := 1 if i.StandardDate.Month > 0 { nzone++ diff --git a/src/vendor/golang.org/x/net/http2/hpack/hpack_test.go b/src/vendor/golang.org/x/net/http2/hpack/hpack_test.go index 6dc69f957990bb..4c7b17bfb14c0b 100644 --- a/src/vendor/golang.org/x/net/http2/hpack/hpack_test.go +++ b/src/vendor/golang.org/x/net/http2/hpack/hpack_test.go @@ -524,6 +524,47 @@ func testDecodeSeries(t *testing.T, size uint32, steps []encAndWant) { } } +func TestHuffmanDecodeExcessPadding(t *testing.T) { + tests := [][]byte{ + {0xff}, // Padding Exceeds 7 bits + {0x1f, 0xff}, // {"a", 1 byte excess padding} + {0x1f, 0xff, 0xff}, // {"a", 2 byte excess padding} + {0x1f, 0xff, 0xff, 0xff}, // {"a", 3 byte excess padding} + {0xff, 0x9f, 0xff, 0xff, 0xff}, // {"a", 29 bit excess padding} + {'R', 0xbc, '0', 0xff, 0xff, 0xff, 0xff}, // Padding ends on partial symbol. + } + for i, in := range tests { + var buf bytes.Buffer + if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman { + t.Errorf("test-%d: decode(%q) = %v; want ErrInvalidHuffman", i, in, err) + } + } +} + +func TestHuffmanDecodeEOS(t *testing.T) { + in := []byte{0xff, 0xff, 0xff, 0xff, 0xfc} // {EOS, "?"} + var buf bytes.Buffer + if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman { + t.Errorf("error = %v; want ErrInvalidHuffman", err) + } +} + +func TestHuffmanDecodeMaxLengthOnTrailingByte(t *testing.T) { + in := []byte{0x00, 0x01} // {"0", "0", "0"} + var buf bytes.Buffer + if err := huffmanDecode(&buf, 2, in); err != ErrStringLength { + t.Errorf("error = %v; want ErrStringLength", err) + } +} + +func TestHuffmanDecodeCorruptPadding(t *testing.T) { + in := []byte{0x00} + var buf bytes.Buffer + if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman { + t.Errorf("error = %v; want ErrInvalidHuffman", err) + } +} + func TestHuffmanDecode(t *testing.T) { tests := []struct { inHex, want string diff --git a/src/vendor/golang.org/x/net/http2/hpack/huffman.go b/src/vendor/golang.org/x/net/http2/hpack/huffman.go index eb4b1f05cd046d..8850e3946770ea 100644 --- a/src/vendor/golang.org/x/net/http2/hpack/huffman.go +++ b/src/vendor/golang.org/x/net/http2/hpack/huffman.go @@ -48,12 +48,16 @@ var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data") // maxLen bytes will return ErrStringLength. func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { n := rootHuffmanNode - cur, nbits := uint(0), uint8(0) + // cur is the bit buffer that has not been fed into n. + // cbits is the number of low order bits in cur that are valid. + // sbits is the number of bits of the symbol prefix being decoded. + cur, cbits, sbits := uint(0), uint8(0), uint8(0) for _, b := range v { cur = cur<<8 | uint(b) - nbits += 8 - for nbits >= 8 { - idx := byte(cur >> (nbits - 8)) + cbits += 8 + sbits += 8 + for cbits >= 8 { + idx := byte(cur >> (cbits - 8)) n = n.children[idx] if n == nil { return ErrInvalidHuffman @@ -63,22 +67,40 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { return ErrStringLength } buf.WriteByte(n.sym) - nbits -= n.codeLen + cbits -= n.codeLen n = rootHuffmanNode + sbits = cbits } else { - nbits -= 8 + cbits -= 8 } } } - for nbits > 0 { - n = n.children[byte(cur<<(8-nbits))] - if n.children != nil || n.codeLen > nbits { + for cbits > 0 { + n = n.children[byte(cur<<(8-cbits))] + if n == nil { + return ErrInvalidHuffman + } + if n.children != nil || n.codeLen > cbits { break } + if maxLen != 0 && buf.Len() == maxLen { + return ErrStringLength + } buf.WriteByte(n.sym) - nbits -= n.codeLen + cbits -= n.codeLen n = rootHuffmanNode + sbits = cbits + } + if sbits > 7 { + // Either there was an incomplete symbol, or overlong padding. + // Both are decoding errors per RFC 7541 section 5.2. + return ErrInvalidHuffman } + if mask := uint(1< -func validHeaderValue(v string) bool { +// +// RFC 7230 says: +// field-value = *( field-content / obs-fold ) +// obj-fold = N/A to http2, and deprecated +// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +// field-vchar = VCHAR / obs-text +// obs-text = %x80-FF +// VCHAR = "any visible [USASCII] character" +// +// http2 further says: "Similarly, HTTP/2 allows header field values +// that are not valid. While most of the values that can be encoded +// will not alter header field parsing, carriage return (CR, ASCII +// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII +// 0x0) might be exploited by an attacker if they are translated +// verbatim. Any request or response that contains a character not +// permitted in a header field value MUST be treated as malformed +// (Section 8.1.2.6). Valid characters are defined by the +// field-content ABNF rule in Section 3.2 of [RFC7230]." +// +// This function does not (yet?) properly handle the rejection of +// strings that begin or end with SP or HTAB. +func ValidHeaderFieldValue(v string) bool { for i := 0; i < len(v); i++ { b := v[i] if isCTL(b) && !isLWS(b) { diff --git a/src/net/http/lex_test.go b/src/vendor/golang.org/x/net/lex/httplex/httplex_test.go similarity index 94% rename from src/net/http/lex_test.go rename to src/vendor/golang.org/x/net/lex/httplex/httplex_test.go index 986fda17dcd6e4..c4ace1991b35dc 100644 --- a/src/net/http/lex_test.go +++ b/src/vendor/golang.org/x/net/lex/httplex/httplex_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package http +package httplex import ( "testing" @@ -24,7 +24,7 @@ func TestIsToken(t *testing.T) { for i := 0; i <= 130; i++ { r := rune(i) expected := isChar(r) && !isCtl(r) && !isSeparator(r) - if isToken(r) != expected { + if IsTokenRune(r) != expected { t.Errorf("isToken(0x%x) = %v", r, !expected) } } @@ -93,7 +93,7 @@ func TestHeaderValuesContainsToken(t *testing.T) { }, } for _, tt := range tests { - got := headerValuesContainsToken(tt.vals, tt.token) + got := HeaderValuesContainsToken(tt.vals, tt.token) if got != tt.want { t.Errorf("headerValuesContainsToken(%q, %q) = %v; want %v", tt.vals, tt.token, got, tt.want) } diff --git a/src/vendor/golang.org/x/net/route/address.go b/src/vendor/golang.org/x/net/route/address.go new file mode 100644 index 00000000000000..206a8371d43565 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/address.go @@ -0,0 +1,269 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +import "runtime" + +// An Addr represents an address associated with packet routing. +type Addr interface { + // Family returns an address family. + Family() int +} + +// A LinkAddr represents a link-layer address. +type LinkAddr struct { + Index int // interface index when attached + Name string // interface name when attached + Addr []byte // link-layer address when attached +} + +// Family implements the Family method of Addr interface. +func (a *LinkAddr) Family() int { return sysAF_LINK } + +func parseLinkAddr(b []byte) (Addr, error) { + if len(b) < 8 { + return nil, errInvalidAddr + } + _, a, err := parseKernelLinkAddr(sysAF_LINK, b[4:]) + if err != nil { + return nil, err + } + a.(*LinkAddr).Index = int(nativeEndian.Uint16(b[2:4])) + return a, nil +} + +// parseKernelLinkAddr parses b as a link-layer address in +// conventional BSD kernel form. +func parseKernelLinkAddr(_ int, b []byte) (int, Addr, error) { + // The encoding looks like the following: + // +----------------------------+ + // | Type (1 octet) | + // +----------------------------+ + // | Name length (1 octet) | + // +----------------------------+ + // | Address length (1 octet) | + // +----------------------------+ + // | Selector length (1 octet) | + // +----------------------------+ + // | Data (variable) | + // +----------------------------+ + // + // On some platforms, all-bit-one of length field means "don't + // care". + nlen, alen, slen := int(b[1]), int(b[2]), int(b[3]) + if nlen == 0xff { + nlen = 0 + } + if alen == 0xff { + alen = 0 + } + if slen == 0xff { + slen = 0 + } + l := 4 + nlen + alen + slen + if len(b) < l { + return 0, nil, errInvalidAddr + } + data := b[4:] + var name string + var addr []byte + if nlen > 0 { + name = string(data[:nlen]) + data = data[nlen:] + } + if alen > 0 { + addr = data[:alen] + data = data[alen:] + } + return l, &LinkAddr{Name: name, Addr: addr}, nil +} + +// An Inet4Addr represents an internet address for IPv4. +type Inet4Addr struct { + IP [4]byte // IP address +} + +// Family implements the Family method of Addr interface. +func (a *Inet4Addr) Family() int { return sysAF_INET } + +// An Inet6Addr represents an internet address for IPv6. +type Inet6Addr struct { + IP [16]byte // IP address + ZoneID int // zone identifier +} + +// Family implements the Family method of Addr interface. +func (a *Inet6Addr) Family() int { return sysAF_INET6 } + +// parseInetAddr parses b as an internet address for IPv4 or IPv6. +func parseInetAddr(af int, b []byte) (Addr, error) { + switch af { + case sysAF_INET: + if len(b) < 16 { + return nil, errInvalidAddr + } + a := &Inet4Addr{} + copy(a.IP[:], b[4:8]) + return a, nil + case sysAF_INET6: + if len(b) < 28 { + return nil, errInvalidAddr + } + a := &Inet6Addr{ZoneID: int(nativeEndian.Uint32(b[24:28]))} + copy(a.IP[:], b[8:24]) + if a.IP[0] == 0xfe && a.IP[1]&0xc0 == 0x80 || a.IP[0] == 0xff && (a.IP[1]&0x0f == 0x01 || a.IP[1]&0x0f == 0x02) { + // KAME based IPv6 protocol stack usually + // embeds the interface index in the + // interface-local or link-local address as + // the kernel-internal form. + id := int(bigEndian.Uint16(a.IP[2:4])) + if id != 0 { + a.ZoneID = id + a.IP[2], a.IP[3] = 0, 0 + } + } + return a, nil + default: + return nil, errInvalidAddr + } +} + +// parseKernelInetAddr parses b as an internet address in conventional +// BSD kernel form. +func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { + // The encoding looks similar to the NLRI encoding. + // +----------------------------+ + // | Length (1 octet) | + // +----------------------------+ + // | Address prefix (variable) | + // +----------------------------+ + // + // The differences between the kernel form and the NLRI + // encoding are: + // + // - The length field of the kernel form indicates the prefix + // length in bytes, not in bits + // + // - In the kernel form, zero value of the length field + // doesn't mean 0.0.0.0/0 or ::/0 + // + // - The kernel form appends leading bytes to the prefix field + // to make the tuple to be conformed with + // the routing message boundary + l := int(b[0]) + if runtime.GOOS == "darwin" { + // On Darwn, an address in the kernel form is also + // used as a message filler. + if l == 0 || len(b) > roundup(l) { + l = roundup(l) + } + } else { + l = roundup(l) + } + if len(b) < l { + return 0, nil, errInvalidAddr + } + // Don't reorder case expressions. + // The case expressions for IPv6 must come first. + const ( + off4 = 4 // offset of in_addr + off6 = 8 // offset of in6_addr + ) + switch { + case b[0] == 28: // size of sockaddr_in6 + a := &Inet6Addr{} + copy(a.IP[:], b[off6:off6+16]) + return int(b[0]), a, nil + case af == sysAF_INET6: + a := &Inet6Addr{} + if l-1 < off6 { + copy(a.IP[:], b[1:l]) + } else { + copy(a.IP[:], b[l-off6:l]) + } + return int(b[0]), a, nil + case b[0] == 16: // size of sockaddr_in + a := &Inet4Addr{} + copy(a.IP[:], b[off4:off4+4]) + return int(b[0]), a, nil + default: // an old fashion, AF_UNSPEC or unknown means AF_INET + a := &Inet4Addr{} + if l-1 < off4 { + copy(a.IP[:], b[1:l]) + } else { + copy(a.IP[:], b[l-off4:l]) + } + return int(b[0]), a, nil + } +} + +// A DefaultAddr represents an address of various operating +// system-specific features. +type DefaultAddr struct { + af int + Raw []byte // raw format of address +} + +// Family implements the Family method of Addr interface. +func (a *DefaultAddr) Family() int { return a.af } + +func parseDefaultAddr(b []byte) (Addr, error) { + if len(b) < 2 || len(b) < int(b[0]) { + return nil, errInvalidAddr + } + a := &DefaultAddr{af: int(b[1]), Raw: b[:b[0]]} + return a, nil +} + +func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) { + var as [sysRTAX_MAX]Addr + af := int(sysAF_UNSPEC) + for i := uint(0); i < sysRTAX_MAX && len(b) >= roundup(0); i++ { + if attrs&(1<> 8) +} + +func (binaryLittleEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (binaryLittleEndian) PutUint32(b []byte, v uint32) { + _ = b[3] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func (binaryLittleEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +type binaryBigEndian struct{} + +func (binaryBigEndian) Uint16(b []byte) uint16 { + _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 + return uint16(b[1]) | uint16(b[0])<<8 +} + +func (binaryBigEndian) PutUint16(b []byte, v uint16) { + _ = b[1] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 8) + b[1] = byte(v) +} + +func (binaryBigEndian) Uint32(b []byte) uint32 { + _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +func (binaryBigEndian) PutUint32(b []byte, v uint32) { + _ = b[3] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) +} + +func (binaryBigEndian) Uint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} diff --git a/src/vendor/golang.org/x/net/route/defs_darwin.go b/src/vendor/golang.org/x/net/route/defs_darwin.go new file mode 100644 index 00000000000000..f452ad14ce6c00 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/defs_darwin.go @@ -0,0 +1,106 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package route + +/* +#include +#include + +#include +#include +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_ROUTE = C.AF_ROUTE + sysAF_LINK = C.AF_LINK + sysAF_INET6 = C.AF_INET6 + + sysNET_RT_DUMP = C.NET_RT_DUMP + sysNET_RT_FLAGS = C.NET_RT_FLAGS + sysNET_RT_IFLIST = C.NET_RT_IFLIST + sysNET_RT_STAT = C.NET_RT_STAT + sysNET_RT_TRASH = C.NET_RT_TRASH + sysNET_RT_IFLIST2 = C.NET_RT_IFLIST2 + sysNET_RT_DUMP2 = C.NET_RT_DUMP2 + sysNET_RT_MAXID = C.NET_RT_MAXID +) + +const ( + sysCTL_MAXNAME = C.CTL_MAXNAME + + sysCTL_UNSPEC = C.CTL_UNSPEC + sysCTL_KERN = C.CTL_KERN + sysCTL_VM = C.CTL_VM + sysCTL_VFS = C.CTL_VFS + sysCTL_NET = C.CTL_NET + sysCTL_DEBUG = C.CTL_DEBUG + sysCTL_HW = C.CTL_HW + sysCTL_MACHDEP = C.CTL_MACHDEP + sysCTL_USER = C.CTL_USER + sysCTL_MAXID = C.CTL_MAXID +) + +const ( + sysRTM_VERSION = C.RTM_VERSION + + sysRTM_ADD = C.RTM_ADD + sysRTM_DELETE = C.RTM_DELETE + sysRTM_CHANGE = C.RTM_CHANGE + sysRTM_GET = C.RTM_GET + sysRTM_LOSING = C.RTM_LOSING + sysRTM_REDIRECT = C.RTM_REDIRECT + sysRTM_MISS = C.RTM_MISS + sysRTM_LOCK = C.RTM_LOCK + sysRTM_OLDADD = C.RTM_OLDADD + sysRTM_OLDDEL = C.RTM_OLDDEL + sysRTM_RESOLVE = C.RTM_RESOLVE + sysRTM_NEWADDR = C.RTM_NEWADDR + sysRTM_DELADDR = C.RTM_DELADDR + sysRTM_IFINFO = C.RTM_IFINFO + sysRTM_NEWMADDR = C.RTM_NEWMADDR + sysRTM_DELMADDR = C.RTM_DELMADDR + sysRTM_IFINFO2 = C.RTM_IFINFO2 + sysRTM_NEWMADDR2 = C.RTM_NEWMADDR2 + sysRTM_GET2 = C.RTM_GET2 + + sysRTA_DST = C.RTA_DST + sysRTA_GATEWAY = C.RTA_GATEWAY + sysRTA_NETMASK = C.RTA_NETMASK + sysRTA_GENMASK = C.RTA_GENMASK + sysRTA_IFP = C.RTA_IFP + sysRTA_IFA = C.RTA_IFA + sysRTA_AUTHOR = C.RTA_AUTHOR + sysRTA_BRD = C.RTA_BRD + + sysRTAX_DST = C.RTAX_DST + sysRTAX_GATEWAY = C.RTAX_GATEWAY + sysRTAX_NETMASK = C.RTAX_NETMASK + sysRTAX_GENMASK = C.RTAX_GENMASK + sysRTAX_IFP = C.RTAX_IFP + sysRTAX_IFA = C.RTAX_IFA + sysRTAX_AUTHOR = C.RTAX_AUTHOR + sysRTAX_BRD = C.RTAX_BRD + sysRTAX_MAX = C.RTAX_MAX +) + +const ( + sizeofIfMsghdrDarwin15 = C.sizeof_struct_if_msghdr + sizeofIfaMsghdrDarwin15 = C.sizeof_struct_ifa_msghdr + sizeofIfmaMsghdrDarwin15 = C.sizeof_struct_ifma_msghdr + sizeofIfMsghdr2Darwin15 = C.sizeof_struct_if_msghdr2 + sizeofIfmaMsghdr2Darwin15 = C.sizeof_struct_ifma_msghdr2 + sizeofIfDataDarwin15 = C.sizeof_struct_if_data + sizeofIfData64Darwin15 = C.sizeof_struct_if_data64 + + sizeofRtMsghdrDarwin15 = C.sizeof_struct_rt_msghdr + sizeofRtMsghdr2Darwin15 = C.sizeof_struct_rt_msghdr2 + sizeofRtMetricsDarwin15 = C.sizeof_struct_rt_metrics +) diff --git a/src/vendor/golang.org/x/net/route/defs_dragonfly.go b/src/vendor/golang.org/x/net/route/defs_dragonfly.go new file mode 100644 index 00000000000000..c737751d76df92 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/defs_dragonfly.go @@ -0,0 +1,105 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package route + +/* +#include +#include + +#include +#include +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_ROUTE = C.AF_ROUTE + sysAF_LINK = C.AF_LINK + sysAF_INET6 = C.AF_INET6 + + sysNET_RT_DUMP = C.NET_RT_DUMP + sysNET_RT_FLAGS = C.NET_RT_FLAGS + sysNET_RT_IFLIST = C.NET_RT_IFLIST + sysNET_RT_MAXID = C.NET_RT_MAXID +) + +const ( + sysCTL_MAXNAME = C.CTL_MAXNAME + + sysCTL_UNSPEC = C.CTL_UNSPEC + sysCTL_KERN = C.CTL_KERN + sysCTL_VM = C.CTL_VM + sysCTL_VFS = C.CTL_VFS + sysCTL_NET = C.CTL_NET + sysCTL_DEBUG = C.CTL_DEBUG + sysCTL_HW = C.CTL_HW + sysCTL_MACHDEP = C.CTL_MACHDEP + sysCTL_USER = C.CTL_USER + sysCTL_P1003_1B = C.CTL_P1003_1B + sysCTL_LWKT = C.CTL_LWKT + sysCTL_MAXID = C.CTL_MAXID +) + +const ( + sysRTM_VERSION = C.RTM_VERSION + + sysRTM_ADD = C.RTM_ADD + sysRTM_DELETE = C.RTM_DELETE + sysRTM_CHANGE = C.RTM_CHANGE + sysRTM_GET = C.RTM_GET + sysRTM_LOSING = C.RTM_LOSING + sysRTM_REDIRECT = C.RTM_REDIRECT + sysRTM_MISS = C.RTM_MISS + sysRTM_LOCK = C.RTM_LOCK + sysRTM_OLDADD = C.RTM_OLDADD + sysRTM_OLDDEL = C.RTM_OLDDEL + sysRTM_RESOLVE = C.RTM_RESOLVE + sysRTM_NEWADDR = C.RTM_NEWADDR + sysRTM_DELADDR = C.RTM_DELADDR + sysRTM_IFINFO = C.RTM_IFINFO + sysRTM_NEWMADDR = C.RTM_NEWMADDR + sysRTM_DELMADDR = C.RTM_DELMADDR + sysRTM_IFANNOUNCE = C.RTM_IFANNOUNCE + sysRTM_IEEE80211 = C.RTM_IEEE80211 + + sysRTA_DST = C.RTA_DST + sysRTA_GATEWAY = C.RTA_GATEWAY + sysRTA_NETMASK = C.RTA_NETMASK + sysRTA_GENMASK = C.RTA_GENMASK + sysRTA_IFP = C.RTA_IFP + sysRTA_IFA = C.RTA_IFA + sysRTA_AUTHOR = C.RTA_AUTHOR + sysRTA_BRD = C.RTA_BRD + sysRTA_MPLS1 = C.RTA_MPLS1 + sysRTA_MPLS2 = C.RTA_MPLS2 + sysRTA_MPLS3 = C.RTA_MPLS3 + + sysRTAX_DST = C.RTAX_DST + sysRTAX_GATEWAY = C.RTAX_GATEWAY + sysRTAX_NETMASK = C.RTAX_NETMASK + sysRTAX_GENMASK = C.RTAX_GENMASK + sysRTAX_IFP = C.RTAX_IFP + sysRTAX_IFA = C.RTAX_IFA + sysRTAX_AUTHOR = C.RTAX_AUTHOR + sysRTAX_BRD = C.RTAX_BRD + sysRTAX_MPLS1 = C.RTAX_MPLS1 + sysRTAX_MPLS2 = C.RTAX_MPLS2 + sysRTAX_MPLS3 = C.RTAX_MPLS3 + sysRTAX_MAX = C.RTAX_MAX +) + +const ( + sizeofIfMsghdrDragonFlyBSD4 = C.sizeof_struct_if_msghdr + sizeofIfaMsghdrDragonFlyBSD4 = C.sizeof_struct_ifa_msghdr + sizeofIfmaMsghdrDragonFlyBSD4 = C.sizeof_struct_ifma_msghdr + sizeofIfAnnouncemsghdrDragonFlyBSD4 = C.sizeof_struct_if_announcemsghdr + + sizeofRtMsghdrDragonFlyBSD4 = C.sizeof_struct_rt_msghdr + sizeofRtMetricsDragonFlyBSD4 = C.sizeof_struct_rt_metrics +) diff --git a/src/vendor/golang.org/x/net/route/defs_freebsd.go b/src/vendor/golang.org/x/net/route/defs_freebsd.go new file mode 100644 index 00000000000000..8f834e81db5557 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/defs_freebsd.go @@ -0,0 +1,329 @@ +// Copyright 2016 The Go 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 route + +/* +#include +#include + +#include +#include +#include + +struct if_data_freebsd7 { + u_char ifi_type; + u_char ifi_physical; + u_char ifi_addrlen; + u_char ifi_hdrlen; + u_char ifi_link_state; + u_char ifi_spare_char1; + u_char ifi_spare_char2; + u_char ifi_datalen; + u_long ifi_mtu; + u_long ifi_metric; + u_long ifi_baudrate; + u_long ifi_ipackets; + u_long ifi_ierrors; + u_long ifi_opackets; + u_long ifi_oerrors; + u_long ifi_collisions; + u_long ifi_ibytes; + u_long ifi_obytes; + u_long ifi_imcasts; + u_long ifi_omcasts; + u_long ifi_iqdrops; + u_long ifi_noproto; + u_long ifi_hwassist; + time_t __ifi_epoch; + struct timeval __ifi_lastchange; +}; + +struct if_data_freebsd8 { + u_char ifi_type; + u_char ifi_physical; + u_char ifi_addrlen; + u_char ifi_hdrlen; + u_char ifi_link_state; + u_char ifi_spare_char1; + u_char ifi_spare_char2; + u_char ifi_datalen; + u_long ifi_mtu; + u_long ifi_metric; + u_long ifi_baudrate; + u_long ifi_ipackets; + u_long ifi_ierrors; + u_long ifi_opackets; + u_long ifi_oerrors; + u_long ifi_collisions; + u_long ifi_ibytes; + u_long ifi_obytes; + u_long ifi_imcasts; + u_long ifi_omcasts; + u_long ifi_iqdrops; + u_long ifi_noproto; + u_long ifi_hwassist; + time_t __ifi_epoch; + struct timeval __ifi_lastchange; +}; + +struct if_data_freebsd9 { + u_char ifi_type; + u_char ifi_physical; + u_char ifi_addrlen; + u_char ifi_hdrlen; + u_char ifi_link_state; + u_char ifi_spare_char1; + u_char ifi_spare_char2; + u_char ifi_datalen; + u_long ifi_mtu; + u_long ifi_metric; + u_long ifi_baudrate; + u_long ifi_ipackets; + u_long ifi_ierrors; + u_long ifi_opackets; + u_long ifi_oerrors; + u_long ifi_collisions; + u_long ifi_ibytes; + u_long ifi_obytes; + u_long ifi_imcasts; + u_long ifi_omcasts; + u_long ifi_iqdrops; + u_long ifi_noproto; + u_long ifi_hwassist; + time_t __ifi_epoch; + struct timeval __ifi_lastchange; +}; + +struct if_data_freebsd10 { + u_char ifi_type; + u_char ifi_physical; + u_char ifi_addrlen; + u_char ifi_hdrlen; + u_char ifi_link_state; + u_char ifi_vhid; + u_char ifi_baudrate_pf; + u_char ifi_datalen; + u_long ifi_mtu; + u_long ifi_metric; + u_long ifi_baudrate; + u_long ifi_ipackets; + u_long ifi_ierrors; + u_long ifi_opackets; + u_long ifi_oerrors; + u_long ifi_collisions; + u_long ifi_ibytes; + u_long ifi_obytes; + u_long ifi_imcasts; + u_long ifi_omcasts; + u_long ifi_iqdrops; + u_long ifi_noproto; + uint64_t ifi_hwassist; + time_t __ifi_epoch; + struct timeval __ifi_lastchange; +}; + +struct if_data_freebsd11 { + uint8_t ifi_type; + uint8_t ifi_physical; + uint8_t ifi_addrlen; + uint8_t ifi_hdrlen; + uint8_t ifi_link_state; + uint8_t ifi_vhid; + uint16_t ifi_datalen; + uint32_t ifi_mtu; + uint32_t ifi_metric; + uint64_t ifi_baudrate; + uint64_t ifi_ipackets; + uint64_t ifi_ierrors; + uint64_t ifi_opackets; + uint64_t ifi_oerrors; + uint64_t ifi_collisions; + uint64_t ifi_ibytes; + uint64_t ifi_obytes; + uint64_t ifi_imcasts; + uint64_t ifi_omcasts; + uint64_t ifi_iqdrops; + uint64_t ifi_oqdrops; + uint64_t ifi_noproto; + uint64_t ifi_hwassist; + union { + time_t tt; + uint64_t ph; + } __ifi_epoch; + union { + struct timeval tv; + struct { + uint64_t ph1; + uint64_t ph2; + } ph; + } __ifi_lastchange; +}; + +struct if_msghdr_freebsd7 { + u_short ifm_msglen; + u_char ifm_version; + u_char ifm_type; + int ifm_addrs; + int ifm_flags; + u_short ifm_index; + struct if_data_freebsd7 ifm_data; +}; + +struct if_msghdr_freebsd8 { + u_short ifm_msglen; + u_char ifm_version; + u_char ifm_type; + int ifm_addrs; + int ifm_flags; + u_short ifm_index; + struct if_data_freebsd8 ifm_data; +}; + +struct if_msghdr_freebsd9 { + u_short ifm_msglen; + u_char ifm_version; + u_char ifm_type; + int ifm_addrs; + int ifm_flags; + u_short ifm_index; + struct if_data_freebsd9 ifm_data; +}; + +struct if_msghdr_freebsd10 { + u_short ifm_msglen; + u_char ifm_version; + u_char ifm_type; + int ifm_addrs; + int ifm_flags; + u_short ifm_index; + struct if_data_freebsd10 ifm_data; +}; + +struct if_msghdr_freebsd11 { + u_short ifm_msglen; + u_char ifm_version; + u_char ifm_type; + int ifm_addrs; + int ifm_flags; + u_short ifm_index; + struct if_data_freebsd11 ifm_data; +}; +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_ROUTE = C.AF_ROUTE + sysAF_LINK = C.AF_LINK + sysAF_INET6 = C.AF_INET6 + + sysNET_RT_DUMP = C.NET_RT_DUMP + sysNET_RT_FLAGS = C.NET_RT_FLAGS + sysNET_RT_IFLIST = C.NET_RT_IFLIST + sysNET_RT_IFMALIST = C.NET_RT_IFMALIST + sysNET_RT_IFLISTL = C.NET_RT_IFLISTL +) + +const ( + sysCTL_MAXNAME = C.CTL_MAXNAME + + sysCTL_UNSPEC = C.CTL_UNSPEC + sysCTL_KERN = C.CTL_KERN + sysCTL_VM = C.CTL_VM + sysCTL_VFS = C.CTL_VFS + sysCTL_NET = C.CTL_NET + sysCTL_DEBUG = C.CTL_DEBUG + sysCTL_HW = C.CTL_HW + sysCTL_MACHDEP = C.CTL_MACHDEP + sysCTL_USER = C.CTL_USER + sysCTL_P1003_1B = C.CTL_P1003_1B +) + +const ( + sysRTM_VERSION = C.RTM_VERSION + + sysRTM_ADD = C.RTM_ADD + sysRTM_DELETE = C.RTM_DELETE + sysRTM_CHANGE = C.RTM_CHANGE + sysRTM_GET = C.RTM_GET + sysRTM_LOSING = C.RTM_LOSING + sysRTM_REDIRECT = C.RTM_REDIRECT + sysRTM_MISS = C.RTM_MISS + sysRTM_LOCK = C.RTM_LOCK + sysRTM_RESOLVE = C.RTM_RESOLVE + sysRTM_NEWADDR = C.RTM_NEWADDR + sysRTM_DELADDR = C.RTM_DELADDR + sysRTM_IFINFO = C.RTM_IFINFO + sysRTM_NEWMADDR = C.RTM_NEWMADDR + sysRTM_DELMADDR = C.RTM_DELMADDR + sysRTM_IFANNOUNCE = C.RTM_IFANNOUNCE + sysRTM_IEEE80211 = C.RTM_IEEE80211 + + sysRTA_DST = C.RTA_DST + sysRTA_GATEWAY = C.RTA_GATEWAY + sysRTA_NETMASK = C.RTA_NETMASK + sysRTA_GENMASK = C.RTA_GENMASK + sysRTA_IFP = C.RTA_IFP + sysRTA_IFA = C.RTA_IFA + sysRTA_AUTHOR = C.RTA_AUTHOR + sysRTA_BRD = C.RTA_BRD + + sysRTAX_DST = C.RTAX_DST + sysRTAX_GATEWAY = C.RTAX_GATEWAY + sysRTAX_NETMASK = C.RTAX_NETMASK + sysRTAX_GENMASK = C.RTAX_GENMASK + sysRTAX_IFP = C.RTAX_IFP + sysRTAX_IFA = C.RTAX_IFA + sysRTAX_AUTHOR = C.RTAX_AUTHOR + sysRTAX_BRD = C.RTAX_BRD + sysRTAX_MAX = C.RTAX_MAX +) + +const ( + sizeofIfMsghdrlFreeBSD10 = C.sizeof_struct_if_msghdrl + sizeofIfaMsghdrFreeBSD10 = C.sizeof_struct_ifa_msghdr + sizeofIfaMsghdrlFreeBSD10 = C.sizeof_struct_ifa_msghdrl + sizeofIfmaMsghdrFreeBSD10 = C.sizeof_struct_ifma_msghdr + sizeofIfAnnouncemsghdrFreeBSD10 = C.sizeof_struct_if_announcemsghdr + + sizeofRtMsghdrFreeBSD10 = C.sizeof_struct_rt_msghdr + sizeofRtMetricsFreeBSD10 = C.sizeof_struct_rt_metrics + + sizeofIfMsghdrFreeBSD7 = C.sizeof_struct_if_msghdr_freebsd7 + sizeofIfMsghdrFreeBSD8 = C.sizeof_struct_if_msghdr_freebsd8 + sizeofIfMsghdrFreeBSD9 = C.sizeof_struct_if_msghdr_freebsd9 + sizeofIfMsghdrFreeBSD10 = C.sizeof_struct_if_msghdr_freebsd10 + sizeofIfMsghdrFreeBSD11 = C.sizeof_struct_if_msghdr_freebsd11 + + sizeofIfDataFreeBSD7 = C.sizeof_struct_if_data_freebsd7 + sizeofIfDataFreeBSD8 = C.sizeof_struct_if_data_freebsd8 + sizeofIfDataFreeBSD9 = C.sizeof_struct_if_data_freebsd9 + sizeofIfDataFreeBSD10 = C.sizeof_struct_if_data_freebsd10 + sizeofIfDataFreeBSD11 = C.sizeof_struct_if_data_freebsd11 + + sizeofIfMsghdrlFreeBSD10Emu = C.sizeof_struct_if_msghdrl + sizeofIfaMsghdrFreeBSD10Emu = C.sizeof_struct_ifa_msghdr + sizeofIfaMsghdrlFreeBSD10Emu = C.sizeof_struct_ifa_msghdrl + sizeofIfmaMsghdrFreeBSD10Emu = C.sizeof_struct_ifma_msghdr + sizeofIfAnnouncemsghdrFreeBSD10Emu = C.sizeof_struct_if_announcemsghdr + + sizeofRtMsghdrFreeBSD10Emu = C.sizeof_struct_rt_msghdr + sizeofRtMetricsFreeBSD10Emu = C.sizeof_struct_rt_metrics + + sizeofIfMsghdrFreeBSD7Emu = C.sizeof_struct_if_msghdr_freebsd7 + sizeofIfMsghdrFreeBSD8Emu = C.sizeof_struct_if_msghdr_freebsd8 + sizeofIfMsghdrFreeBSD9Emu = C.sizeof_struct_if_msghdr_freebsd9 + sizeofIfMsghdrFreeBSD10Emu = C.sizeof_struct_if_msghdr_freebsd10 + sizeofIfMsghdrFreeBSD11Emu = C.sizeof_struct_if_msghdr_freebsd11 + + sizeofIfDataFreeBSD7Emu = C.sizeof_struct_if_data_freebsd7 + sizeofIfDataFreeBSD8Emu = C.sizeof_struct_if_data_freebsd8 + sizeofIfDataFreeBSD9Emu = C.sizeof_struct_if_data_freebsd9 + sizeofIfDataFreeBSD10Emu = C.sizeof_struct_if_data_freebsd10 + sizeofIfDataFreeBSD11Emu = C.sizeof_struct_if_data_freebsd11 +) diff --git a/src/vendor/golang.org/x/net/route/defs_netbsd.go b/src/vendor/golang.org/x/net/route/defs_netbsd.go new file mode 100644 index 00000000000000..b18d85e0165db9 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/defs_netbsd.go @@ -0,0 +1,104 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package route + +/* +#include +#include + +#include +#include +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_ROUTE = C.AF_ROUTE + sysAF_LINK = C.AF_LINK + sysAF_INET6 = C.AF_INET6 + + sysNET_RT_DUMP = C.NET_RT_DUMP + sysNET_RT_FLAGS = C.NET_RT_FLAGS + sysNET_RT_IFLIST = C.NET_RT_IFLIST + sysNET_RT_MAXID = C.NET_RT_MAXID +) + +const ( + sysCTL_MAXNAME = C.CTL_MAXNAME + + sysCTL_UNSPEC = C.CTL_UNSPEC + sysCTL_KERN = C.CTL_KERN + sysCTL_VM = C.CTL_VM + sysCTL_VFS = C.CTL_VFS + sysCTL_NET = C.CTL_NET + sysCTL_DEBUG = C.CTL_DEBUG + sysCTL_HW = C.CTL_HW + sysCTL_MACHDEP = C.CTL_MACHDEP + sysCTL_USER = C.CTL_USER + sysCTL_DDB = C.CTL_DDB + sysCTL_PROC = C.CTL_PROC + sysCTL_VENDOR = C.CTL_VENDOR + sysCTL_EMUL = C.CTL_EMUL + sysCTL_SECURITY = C.CTL_SECURITY + sysCTL_MAXID = C.CTL_MAXID +) + +const ( + sysRTM_VERSION = C.RTM_VERSION + + sysRTM_ADD = C.RTM_ADD + sysRTM_DELETE = C.RTM_DELETE + sysRTM_CHANGE = C.RTM_CHANGE + sysRTM_GET = C.RTM_GET + sysRTM_LOSING = C.RTM_LOSING + sysRTM_REDIRECT = C.RTM_REDIRECT + sysRTM_MISS = C.RTM_MISS + sysRTM_LOCK = C.RTM_LOCK + sysRTM_OLDADD = C.RTM_OLDADD + sysRTM_OLDDEL = C.RTM_OLDDEL + sysRTM_RESOLVE = C.RTM_RESOLVE + sysRTM_NEWADDR = C.RTM_NEWADDR + sysRTM_DELADDR = C.RTM_DELADDR + sysRTM_IFANNOUNCE = C.RTM_IFANNOUNCE + sysRTM_IEEE80211 = C.RTM_IEEE80211 + sysRTM_SETGATE = C.RTM_SETGATE + sysRTM_LLINFO_UPD = C.RTM_LLINFO_UPD + sysRTM_IFINFO = C.RTM_IFINFO + sysRTM_CHGADDR = C.RTM_CHGADDR + + sysRTA_DST = C.RTA_DST + sysRTA_GATEWAY = C.RTA_GATEWAY + sysRTA_NETMASK = C.RTA_NETMASK + sysRTA_GENMASK = C.RTA_GENMASK + sysRTA_IFP = C.RTA_IFP + sysRTA_IFA = C.RTA_IFA + sysRTA_AUTHOR = C.RTA_AUTHOR + sysRTA_BRD = C.RTA_BRD + sysRTA_TAG = C.RTA_TAG + + sysRTAX_DST = C.RTAX_DST + sysRTAX_GATEWAY = C.RTAX_GATEWAY + sysRTAX_NETMASK = C.RTAX_NETMASK + sysRTAX_GENMASK = C.RTAX_GENMASK + sysRTAX_IFP = C.RTAX_IFP + sysRTAX_IFA = C.RTAX_IFA + sysRTAX_AUTHOR = C.RTAX_AUTHOR + sysRTAX_BRD = C.RTAX_BRD + sysRTAX_TAG = C.RTAX_TAG + sysRTAX_MAX = C.RTAX_MAX +) + +const ( + sizeofIfMsghdrNetBSD7 = C.sizeof_struct_if_msghdr + sizeofIfaMsghdrNetBSD7 = C.sizeof_struct_ifa_msghdr + sizeofIfAnnouncemsghdrNetBSD7 = C.sizeof_struct_if_announcemsghdr + + sizeofRtMsghdrNetBSD7 = C.sizeof_struct_rt_msghdr + sizeofRtMetricsNetBSD7 = C.sizeof_struct_rt_metrics +) diff --git a/src/vendor/golang.org/x/net/route/defs_openbsd.go b/src/vendor/golang.org/x/net/route/defs_openbsd.go new file mode 100644 index 00000000000000..5df7a43bc3ee64 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/defs_openbsd.go @@ -0,0 +1,93 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package route + +/* +#include +#include + +#include +#include +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_ROUTE = C.AF_ROUTE + sysAF_LINK = C.AF_LINK + sysAF_INET6 = C.AF_INET6 + + sysNET_RT_DUMP = C.NET_RT_DUMP + sysNET_RT_FLAGS = C.NET_RT_FLAGS + sysNET_RT_IFLIST = C.NET_RT_IFLIST + sysNET_RT_STATS = C.NET_RT_STATS + sysNET_RT_TABLE = C.NET_RT_TABLE + sysNET_RT_IFNAMES = C.NET_RT_IFNAMES + sysNET_RT_MAXID = C.NET_RT_MAXID +) + +const ( + sysCTL_MAXNAME = C.CTL_MAXNAME + + sysCTL_UNSPEC = C.CTL_UNSPEC + sysCTL_KERN = C.CTL_KERN + sysCTL_VM = C.CTL_VM + sysCTL_FS = C.CTL_FS + sysCTL_NET = C.CTL_NET + sysCTL_DEBUG = C.CTL_DEBUG + sysCTL_HW = C.CTL_HW + sysCTL_MACHDEP = C.CTL_MACHDEP + sysCTL_DDB = C.CTL_DDB + sysCTL_VFS = C.CTL_VFS + sysCTL_MAXID = C.CTL_MAXID +) + +const ( + sysRTM_VERSION = C.RTM_VERSION + + sysRTM_ADD = C.RTM_ADD + sysRTM_DELETE = C.RTM_DELETE + sysRTM_CHANGE = C.RTM_CHANGE + sysRTM_GET = C.RTM_GET + sysRTM_LOSING = C.RTM_LOSING + sysRTM_REDIRECT = C.RTM_REDIRECT + sysRTM_MISS = C.RTM_MISS + sysRTM_LOCK = C.RTM_LOCK + sysRTM_RESOLVE = C.RTM_RESOLVE + sysRTM_NEWADDR = C.RTM_NEWADDR + sysRTM_DELADDR = C.RTM_DELADDR + sysRTM_IFINFO = C.RTM_IFINFO + sysRTM_IFANNOUNCE = C.RTM_IFANNOUNCE + sysRTM_DESYNC = C.RTM_DESYNC + + sysRTA_DST = C.RTA_DST + sysRTA_GATEWAY = C.RTA_GATEWAY + sysRTA_NETMASK = C.RTA_NETMASK + sysRTA_GENMASK = C.RTA_GENMASK + sysRTA_IFP = C.RTA_IFP + sysRTA_IFA = C.RTA_IFA + sysRTA_AUTHOR = C.RTA_AUTHOR + sysRTA_BRD = C.RTA_BRD + sysRTA_SRC = C.RTA_SRC + sysRTA_SRCMASK = C.RTA_SRCMASK + sysRTA_LABEL = C.RTA_LABEL + + sysRTAX_DST = C.RTAX_DST + sysRTAX_GATEWAY = C.RTAX_GATEWAY + sysRTAX_NETMASK = C.RTAX_NETMASK + sysRTAX_GENMASK = C.RTAX_GENMASK + sysRTAX_IFP = C.RTAX_IFP + sysRTAX_IFA = C.RTAX_IFA + sysRTAX_AUTHOR = C.RTAX_AUTHOR + sysRTAX_BRD = C.RTAX_BRD + sysRTAX_SRC = C.RTAX_SRC + sysRTAX_SRCMASK = C.RTAX_SRCMASK + sysRTAX_LABEL = C.RTAX_LABEL + sysRTAX_MAX = C.RTAX_MAX +) diff --git a/src/vendor/golang.org/x/net/route/interface.go b/src/vendor/golang.org/x/net/route/interface.go new file mode 100644 index 00000000000000..854906d9c42913 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/interface.go @@ -0,0 +1,64 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +// An InterfaceMessage represents an interface message. +type InterfaceMessage struct { + Version int // message version + Type int // message type + Flags int // interface flags + Index int // interface index + Name string // interface name + Addrs []Addr // addresses + + extOff int // offset of header extension + raw []byte // raw message +} + +// An InterfaceAddrMessage represents an interface address message. +type InterfaceAddrMessage struct { + Version int // message version + Type int // message type + Flags int // interface flags + Index int // interface index + Addrs []Addr // addresses + + raw []byte // raw message +} + +// Sys implements the Sys method of Message interface. +func (m *InterfaceAddrMessage) Sys() []Sys { return nil } + +// An InterfaceMulticastAddrMessage represents an interface multicast +// address message. +type InterfaceMulticastAddrMessage struct { + Version int // message version + Type int // messsage type + Flags int // interface flags + Index int // interface index + Addrs []Addr // addresses + + raw []byte // raw message +} + +// Sys implements the Sys method of Message interface. +func (m *InterfaceMulticastAddrMessage) Sys() []Sys { return nil } + +// An InterfaceAnnounceMessage represents an interface announcement +// message. +type InterfaceAnnounceMessage struct { + Version int // message version + Type int // message type + Index int // interface index + Name string // interface name + What int // what type of announcement + + raw []byte // raw message +} + +// Sys implements the Sys method of Message interface. +func (m *InterfaceAnnounceMessage) Sys() []Sys { return nil } diff --git a/src/vendor/golang.org/x/net/route/interface_announce.go b/src/vendor/golang.org/x/net/route/interface_announce.go new file mode 100644 index 00000000000000..520d657b578fce --- /dev/null +++ b/src/vendor/golang.org/x/net/route/interface_announce.go @@ -0,0 +1,32 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly freebsd netbsd + +package route + +func (w *wireFormat) parseInterfaceAnnounceMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &InterfaceAnnounceMessage{ + Version: int(b[2]), + Type: int(b[3]), + Index: int(nativeEndian.Uint16(b[4:6])), + What: int(nativeEndian.Uint16(b[22:24])), + raw: b[:l], + } + for i := 0; i < 16; i++ { + if b[6+i] != 0 { + continue + } + m.Name = string(b[6 : 6+i]) + break + } + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/interface_classic.go b/src/vendor/golang.org/x/net/route/interface_classic.go new file mode 100644 index 00000000000000..ac4e7a6805afdf --- /dev/null +++ b/src/vendor/golang.org/x/net/route/interface_classic.go @@ -0,0 +1,66 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly netbsd + +package route + +import "runtime" + +func (w *wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + attrs := uint(nativeEndian.Uint32(b[4:8])) + if attrs&sysRTA_IFP == 0 { + return nil, nil + } + m := &InterfaceMessage{ + Version: int(b[2]), + Type: int(b[3]), + Addrs: make([]Addr, sysRTAX_MAX), + Flags: int(nativeEndian.Uint32(b[8:12])), + Index: int(nativeEndian.Uint16(b[12:14])), + extOff: w.extOff, + raw: b[:l], + } + a, err := parseLinkAddr(b[w.bodyOff:]) + if err != nil { + return nil, err + } + m.Addrs[sysRTAX_IFP] = a + m.Name = a.(*LinkAddr).Name + return m, nil +} + +func (w *wireFormat) parseInterfaceAddrMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &InterfaceAddrMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[8:12])), + raw: b[:l], + } + if runtime.GOOS == "netbsd" { + m.Index = int(nativeEndian.Uint16(b[16:18])) + } else { + m.Index = int(nativeEndian.Uint16(b[12:14])) + } + var err error + m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[4:8])), parseKernelInetAddr, b[w.bodyOff:]) + if err != nil { + return nil, err + } + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/interface_freebsd.go b/src/vendor/golang.org/x/net/route/interface_freebsd.go new file mode 100644 index 00000000000000..c83053915da52b --- /dev/null +++ b/src/vendor/golang.org/x/net/route/interface_freebsd.go @@ -0,0 +1,78 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, error) { + var extOff, bodyOff int + if typ == sysNET_RT_IFLISTL { + if len(b) < 20 { + return nil, errMessageTooShort + } + extOff = int(nativeEndian.Uint16(b[18:20])) + bodyOff = int(nativeEndian.Uint16(b[16:18])) + } else { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + extOff = w.extOff + bodyOff = w.bodyOff + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + attrs := uint(nativeEndian.Uint32(b[4:8])) + if attrs&sysRTA_IFP == 0 { + return nil, nil + } + m := &InterfaceMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[8:12])), + Index: int(nativeEndian.Uint16(b[12:14])), + Addrs: make([]Addr, sysRTAX_MAX), + extOff: extOff, + raw: b[:l], + } + a, err := parseLinkAddr(b[bodyOff:]) + if err != nil { + return nil, err + } + m.Addrs[sysRTAX_IFP] = a + m.Name = a.(*LinkAddr).Name + return m, nil +} + +func (w *wireFormat) parseInterfaceAddrMessage(typ RIBType, b []byte) (Message, error) { + var bodyOff int + if typ == sysNET_RT_IFLISTL { + if len(b) < 24 { + return nil, errMessageTooShort + } + bodyOff = int(nativeEndian.Uint16(b[16:18])) + } else { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + bodyOff = w.bodyOff + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &InterfaceAddrMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[8:12])), + Index: int(nativeEndian.Uint16(b[12:14])), + raw: b[:l], + } + var err error + m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[4:8])), parseKernelInetAddr, b[bodyOff:]) + if err != nil { + return nil, err + } + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/interface_multicast.go b/src/vendor/golang.org/x/net/route/interface_multicast.go new file mode 100644 index 00000000000000..1e99a9cc64b9ad --- /dev/null +++ b/src/vendor/golang.org/x/net/route/interface_multicast.go @@ -0,0 +1,30 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd + +package route + +func (w *wireFormat) parseInterfaceMulticastAddrMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &InterfaceMulticastAddrMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[8:12])), + Index: int(nativeEndian.Uint16(b[12:14])), + raw: b[:l], + } + var err error + m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[4:8])), parseKernelInetAddr, b[w.bodyOff:]) + if err != nil { + return nil, err + } + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/interface_openbsd.go b/src/vendor/golang.org/x/net/route/interface_openbsd.go new file mode 100644 index 00000000000000..24451d8ca12067 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/interface_openbsd.go @@ -0,0 +1,83 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < 32 { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + attrs := uint(nativeEndian.Uint32(b[12:16])) + if attrs&sysRTA_IFP == 0 { + return nil, nil + } + m := &InterfaceMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[16:20])), + Index: int(nativeEndian.Uint16(b[6:8])), + Addrs: make([]Addr, sysRTAX_MAX), + raw: b[:l], + } + a, err := parseLinkAddr(b[int(nativeEndian.Uint16(b[4:6])):]) + if err != nil { + return nil, err + } + m.Addrs[sysRTAX_IFP] = a + m.Name = a.(*LinkAddr).Name + return m, nil +} + +func (*wireFormat) parseInterfaceAddrMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < 24 { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + bodyOff := int(nativeEndian.Uint16(b[4:6])) + m := &InterfaceAddrMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[12:16])), + Index: int(nativeEndian.Uint16(b[6:8])), + raw: b[:l], + } + var err error + m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[bodyOff:]) + if err != nil { + return nil, err + } + return m, nil +} + +func (*wireFormat) parseInterfaceAnnounceMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < 26 { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &InterfaceAnnounceMessage{ + Version: int(b[2]), + Type: int(b[3]), + Index: int(nativeEndian.Uint16(b[6:8])), + What: int(nativeEndian.Uint16(b[8:10])), + raw: b[:l], + } + for i := 0; i < 16; i++ { + if b[10+i] != 0 { + continue + } + m.Name = string(b[10 : 10+i]) + break + } + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/message.go b/src/vendor/golang.org/x/net/route/message.go new file mode 100644 index 00000000000000..27cbf6b77ace75 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/message.go @@ -0,0 +1,70 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +// A Message represents a routing message. +// +// Note: This interface will be changed to support Marshal method in +// future version. +type Message interface { + // Sys returns operating system-specific information. + Sys() []Sys +} + +// A Sys reprensents operating system-specific information. +type Sys interface { + // SysType returns a type of operating system-specific + // information. + SysType() SysType +} + +// A SysType represents a type of operating system-specific +// information. +type SysType int + +const ( + SysMetrics SysType = iota + SysStats +) + +// ParseRIB parses b as a routing information base and returns a list +// of routing messages. +func ParseRIB(typ RIBType, b []byte) ([]Message, error) { + if !typ.parseable() { + return nil, errUnsupportedMessage + } + var msgs []Message + nmsgs, nskips := 0, 0 + for len(b) > 4 { + nmsgs++ + l := int(nativeEndian.Uint16(b[:2])) + if b[2] != sysRTM_VERSION { + b = b[l:] + continue + } + mtyp := int(b[3]) + if fn, ok := parseFns[mtyp]; !ok { + nskips++ + } else { + m, err := fn(typ, b) + if err != nil { + return nil, err + } + if m == nil { + nskips++ + } else { + msgs = append(msgs, m) + } + } + b = b[l:] + } + // We failed to parse any of the messages - version mismatch? + if nmsgs != len(msgs)+nskips { + return nil, errMessageMismatch + } + return msgs, nil +} diff --git a/src/vendor/golang.org/x/net/route/message_darwin_test.go b/src/vendor/golang.org/x/net/route/message_darwin_test.go new file mode 100644 index 00000000000000..3fdd12df5537e2 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/message_darwin_test.go @@ -0,0 +1,27 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +import "testing" + +func TestFetchAndParseRIBOnDarwin(t *testing.T) { + for _, af := range []int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} { + for _, typ := range []RIBType{sysNET_RT_FLAGS, sysNET_RT_DUMP2, sysNET_RT_IFLIST2} { + ms, err := fetchAndParseRIB(af, typ) + if err != nil { + t.Error(err) + continue + } + ss, err := msgs(ms).validate() + if err != nil { + t.Errorf("%v %d %v", addrFamily(af), typ, err) + continue + } + for _, s := range ss { + t.Log(s) + } + } + } +} diff --git a/src/vendor/golang.org/x/net/route/message_freebsd_test.go b/src/vendor/golang.org/x/net/route/message_freebsd_test.go new file mode 100644 index 00000000000000..6d03d000a82fda --- /dev/null +++ b/src/vendor/golang.org/x/net/route/message_freebsd_test.go @@ -0,0 +1,106 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +import ( + "testing" + "time" + "unsafe" +) + +func TestFetchAndParseRIBOnFreeBSD(t *testing.T) { + for _, af := range []int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} { + for _, typ := range []RIBType{sysNET_RT_IFMALIST} { + ms, err := fetchAndParseRIB(af, typ) + if err != nil { + t.Error(err) + continue + } + ss, err := msgs(ms).validate() + if err != nil { + t.Errorf("%v %d %v", addrFamily(af), typ, err) + continue + } + for _, s := range ss { + t.Log(s) + } + } + } +} + +func TestFetchAndParseRIBOnFreeBSD10AndAbove(t *testing.T) { + if _, err := FetchRIB(sysAF_UNSPEC, sysNET_RT_IFLISTL, 0); err != nil { + t.Skip("NET_RT_LISTL not supported") + } + var p uintptr + if kernelAlign != int(unsafe.Sizeof(p)) { + t.Skip("NET_RT_LIST vs. NET_RT_LISTL doesn't work for 386 emulation on amd64") + } + + var tests = [2]struct { + typ RIBType + b []byte + msgs []Message + ss []string + }{ + {typ: sysNET_RT_IFLIST}, + {typ: sysNET_RT_IFLISTL}, + } + for _, af := range []int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} { + var lastErr error + for i := 0; i < 3; i++ { + for j := range tests { + var err error + if tests[j].b, err = FetchRIB(af, tests[j].typ, 0); err != nil { + lastErr = err + time.Sleep(10 * time.Millisecond) + } + } + if lastErr == nil { + break + } + } + if lastErr != nil { + t.Error(af, lastErr) + continue + } + for i := range tests { + var err error + if tests[i].msgs, err = ParseRIB(tests[i].typ, tests[i].b); err != nil { + lastErr = err + t.Error(af, err) + } + } + if lastErr != nil { + continue + } + for i := range tests { + var err error + tests[i].ss, err = msgs(tests[i].msgs).validate() + if err != nil { + lastErr = err + t.Error(af, err) + } + for _, s := range tests[i].ss { + t.Log(s) + } + } + if lastErr != nil { + continue + } + for i := len(tests) - 1; i > 0; i-- { + if len(tests[i].ss) != len(tests[i-1].ss) { + t.Errorf("got %v; want %v", tests[i].ss, tests[i-1].ss) + continue + } + for j, s1 := range tests[i].ss { + s0 := tests[i-1].ss[j] + if s1 != s0 { + t.Errorf("got %s; want %s", s1, s0) + } + } + } + } +} diff --git a/src/vendor/golang.org/x/net/route/message_test.go b/src/vendor/golang.org/x/net/route/message_test.go new file mode 100644 index 00000000000000..a1263d8f25e4dc --- /dev/null +++ b/src/vendor/golang.org/x/net/route/message_test.go @@ -0,0 +1,95 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +import ( + "os" + "syscall" + "testing" + "time" +) + +func TestFetchAndParseRIB(t *testing.T) { + for _, af := range []int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} { + for _, typ := range []RIBType{sysNET_RT_DUMP, sysNET_RT_IFLIST} { + ms, err := fetchAndParseRIB(af, typ) + if err != nil { + t.Error(err) + continue + } + ss, err := msgs(ms).validate() + if err != nil { + t.Errorf("%v %d %v", addrFamily(af), typ, err) + continue + } + for _, s := range ss { + t.Log(s) + } + } + } +} + +func TestMonitorAndParseRIB(t *testing.T) { + if testing.Short() || os.Getuid() != 0 { + t.Skip("must be root") + } + + // We suppose that using an IPv4 link-local address and the + // dot1Q ID for Token Ring and FDDI doesn't harm anyone. + pv := &propVirtual{addr: "169.254.0.1", mask: "255.255.255.0"} + if err := pv.configure(1002); err != nil { + t.Skip(err) + } + if err := pv.setup(); err != nil { + t.Skip(err) + } + pv.teardown() + + s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) + if err != nil { + t.Fatal(err) + } + defer syscall.Close(s) + + go func() { + b := make([]byte, os.Getpagesize()) + for { + n, err := syscall.Read(s, b) + if err != nil { + return + } + ms, err := ParseRIB(0, b[:n]) + if err != nil { + t.Error(err) + return + } + ss, err := msgs(ms).validate() + if err != nil { + t.Error(err) + return + } + for _, s := range ss { + t.Log(s) + } + } + }() + + for _, vid := range []int{1002, 1003, 1004, 1005} { + pv := &propVirtual{addr: "169.254.0.1", mask: "255.255.255.0"} + if err := pv.configure(vid); err != nil { + t.Fatal(err) + } + if err := pv.setup(); err != nil { + t.Fatal(err) + } + time.Sleep(200 * time.Millisecond) + if err := pv.teardown(); err != nil { + t.Fatal(err) + } + time.Sleep(200 * time.Millisecond) + } +} diff --git a/src/vendor/golang.org/x/net/route/route.go b/src/vendor/golang.org/x/net/route/route.go new file mode 100644 index 00000000000000..c986e29ebc94c3 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/route.go @@ -0,0 +1,74 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +// Package route provides basic functions for the manipulation of +// packet routing facilities on BSD variants. +// +// The package supports any version of Darwin, any version of +// DragonFly BSD, FreeBSD 7 through 11, NetBSD 6 and above, and +// OpenBSD 5.6 and above. +package route + +import ( + "errors" + "os" + "syscall" +) + +var ( + errUnsupportedMessage = errors.New("unsupported message") + errMessageMismatch = errors.New("message mismatch") + errMessageTooShort = errors.New("message too short") + errInvalidMessage = errors.New("invalid message") + errInvalidAddr = errors.New("invalid address") +) + +// A RouteMessage represents a message conveying an address prefix, a +// nexthop address and an output interface. +type RouteMessage struct { + Version int // message version + Type int // message type + Flags int // route flags + Index int // interface index when atatched + Addrs []Addr // addresses + + extOff int // offset of header extension + raw []byte // raw message +} + +// A RIBType reprensents a type of routing information base. +type RIBType int + +const ( + RIBTypeRoute RIBType = syscall.NET_RT_DUMP + RIBTypeInterface RIBType = syscall.NET_RT_IFLIST +) + +// FetchRIB fetches a routing information base from the operating +// system. +// +// The provided af must be an address family. +// +// The provided arg must be a RIBType-specific argument. +// When RIBType is related to routes, arg might be a set of route +// flags. When RIBType is related to network interfaces, arg might be +// an interface index or a set of interface flags. In most cases, zero +// means a wildcard. +func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) { + mib := [6]int32{sysCTL_NET, sysAF_ROUTE, 0, int32(af), int32(typ), int32(arg)} + n := uintptr(0) + if err := sysctl(mib[:], nil, &n, nil, 0); err != nil { + return nil, os.NewSyscallError("sysctl", err) + } + if n == 0 { + return nil, nil + } + b := make([]byte, n) + if err := sysctl(mib[:], &b[0], &n, nil, 0); err != nil { + return nil, os.NewSyscallError("sysctl", err) + } + return b[:n], nil +} diff --git a/src/vendor/golang.org/x/net/route/route_classic.go b/src/vendor/golang.org/x/net/route/route_classic.go new file mode 100644 index 00000000000000..d333c6aa526b19 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/route_classic.go @@ -0,0 +1,31 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd + +package route + +func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) { + if len(b) < w.bodyOff { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &RouteMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[8:12])), + Index: int(nativeEndian.Uint16(b[4:6])), + extOff: w.extOff, + raw: b[:l], + } + var err error + m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[w.bodyOff:]) + if err != nil { + return nil, err + } + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/route_openbsd.go b/src/vendor/golang.org/x/net/route/route_openbsd.go new file mode 100644 index 00000000000000..b07862f04d3f50 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/route_openbsd.go @@ -0,0 +1,28 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) { + if len(b) < 40 { + return nil, errMessageTooShort + } + l := int(nativeEndian.Uint16(b[:2])) + if len(b) < l { + return nil, errInvalidMessage + } + m := &RouteMessage{ + Version: int(b[2]), + Type: int(b[3]), + Flags: int(nativeEndian.Uint32(b[16:20])), + Index: int(nativeEndian.Uint16(b[6:8])), + raw: b[:l], + } + as, err := parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[int(nativeEndian.Uint16(b[4:6])):]) + if err != nil { + return nil, err + } + m.Addrs = as + return m, nil +} diff --git a/src/vendor/golang.org/x/net/route/route_test.go b/src/vendor/golang.org/x/net/route/route_test.go new file mode 100644 index 00000000000000..99f57b712d9c38 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/route_test.go @@ -0,0 +1,385 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +import ( + "fmt" + "os/exec" + "runtime" + "time" +) + +func (m *RouteMessage) String() string { + return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[12:16]))) +} + +func (m *InterfaceMessage) String() string { + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + return fmt.Sprintf("%s", attrs) +} + +func (m *InterfaceAddrMessage) String() string { + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + return fmt.Sprintf("%s", attrs) +} + +func (m *InterfaceMulticastAddrMessage) String() string { + return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[4:8]))) +} + +func (m *InterfaceAnnounceMessage) String() string { + what := "" + switch m.What { + case 0: + what = "arrival" + case 1: + what = "departure" + } + return fmt.Sprintf("(%d %s %s)", m.Index, m.Name, what) +} + +func (m *InterfaceMetrics) String() string { + return fmt.Sprintf("(type=%d mtu=%d)", m.Type, m.MTU) +} + +func (m *RouteMetrics) String() string { + return fmt.Sprintf("(pmtu=%d)", m.PathMTU) +} + +type addrAttrs uint + +var addrAttrNames = [...]string{ + "dst", + "gateway", + "netmask", + "genmask", + "ifp", + "ifa", + "author", + "brd", + "df:mpls1-n:tag-o:src", // mpls1 for dragonfly, tag for netbsd, src for openbsd + "df:mpls2-o:srcmask", // mpls2 for dragonfly, srcmask for openbsd + "df:mpls3-o:label", // mpls3 for dragonfly, label for openbsd +} + +func (attrs addrAttrs) String() string { + var s string + for i, name := range addrAttrNames { + if attrs&(1<" + } + return s +} + +type msgs []Message + +func (ms msgs) validate() ([]string, error) { + var ss []string + for _, m := range ms { + switch m := m.(type) { + case *RouteMessage: + if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[12:16]))); err != nil { + return nil, err + } + sys := m.Sys() + if sys == nil { + return nil, fmt.Errorf("no sys for %s", m.String()) + } + ss = append(ss, m.String()+" "+syss(sys).String()+" "+addrs(m.Addrs).String()) + case *InterfaceMessage: + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + if err := addrs(m.Addrs).match(attrs); err != nil { + return nil, err + } + sys := m.Sys() + if sys == nil { + return nil, fmt.Errorf("no sys for %s", m.String()) + } + ss = append(ss, m.String()+" "+syss(sys).String()+" "+addrs(m.Addrs).String()) + case *InterfaceAddrMessage: + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + if err := addrs(m.Addrs).match(attrs); err != nil { + return nil, err + } + ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) + case *InterfaceMulticastAddrMessage: + if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[4:8]))); err != nil { + return nil, err + } + ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) + case *InterfaceAnnounceMessage: + ss = append(ss, m.String()) + default: + ss = append(ss, fmt.Sprintf("%+v", m)) + } + } + return ss, nil +} + +type syss []Sys + +func (sys syss) String() string { + var s string + for _, sy := range sys { + switch sy := sy.(type) { + case *InterfaceMetrics: + if len(s) > 0 { + s += " " + } + s += sy.String() + case *RouteMetrics: + if len(s) > 0 { + s += " " + } + s += sy.String() + } + } + return s +} + +type addrFamily int + +func (af addrFamily) String() string { + switch af { + case sysAF_UNSPEC: + return "unspec" + case sysAF_LINK: + return "link" + case sysAF_INET: + return "inet4" + case sysAF_INET6: + return "inet6" + default: + return fmt.Sprintf("%d", af) + } +} + +const hexDigit = "0123456789abcdef" + +type llAddr []byte + +func (a llAddr) String() string { + if len(a) == 0 { + return "" + } + buf := make([]byte, 0, len(a)*3-1) + for i, b := range a { + if i > 0 { + buf = append(buf, ':') + } + buf = append(buf, hexDigit[b>>4]) + buf = append(buf, hexDigit[b&0xF]) + } + return string(buf) +} + +type ipAddr []byte + +func (a ipAddr) String() string { + if len(a) == 0 { + return "" + } + if len(a) == 4 { + return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3]) + } + if len(a) == 16 { + return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]) + } + s := make([]byte, len(a)*2) + for i, tn := range a { + s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf] + } + return string(s) +} + +func (a *LinkAddr) String() string { + name := a.Name + if name == "" { + name = "" + } + lla := llAddr(a.Addr).String() + if lla == "" { + lla = "" + } + return fmt.Sprintf("(%v %d %s %s)", addrFamily(a.Family()), a.Index, name, lla) +} + +func (a Inet4Addr) String() string { + return fmt.Sprintf("(%v %v)", addrFamily(a.Family()), ipAddr(a.IP[:])) +} + +func (a *Inet6Addr) String() string { + return fmt.Sprintf("(%v %v %d)", addrFamily(a.Family()), ipAddr(a.IP[:]), a.ZoneID) +} + +func (a *DefaultAddr) String() string { + return fmt.Sprintf("(%v %s)", addrFamily(a.Family()), ipAddr(a.Raw[2:]).String()) +} + +type addrs []Addr + +func (as addrs) String() string { + var s string + for _, a := range as { + if a == nil { + continue + } + if len(s) > 0 { + s += " " + } + switch a := a.(type) { + case *LinkAddr: + s += a.String() + case *Inet4Addr: + s += a.String() + case *Inet6Addr: + s += a.String() + case *DefaultAddr: + s += a.String() + } + } + if s == "" { + return "" + } + return s +} + +func (as addrs) match(attrs addrAttrs) error { + var ts addrAttrs + af := sysAF_UNSPEC + for i := range as { + if as[i] != nil { + ts |= 1 << uint(i) + } + switch as[i].(type) { + case *Inet4Addr: + if af == sysAF_UNSPEC { + af = sysAF_INET + } + if af != sysAF_INET { + return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) + } + case *Inet6Addr: + if af == sysAF_UNSPEC { + af = sysAF_INET6 + } + if af != sysAF_INET6 { + return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) + } + } + } + if ts != attrs && ts > attrs { + return fmt.Errorf("%v not included in %v", ts, attrs) + } + return nil +} + +func fetchAndParseRIB(af int, typ RIBType) ([]Message, error) { + var err error + var b []byte + for i := 0; i < 3; i++ { + if b, err = FetchRIB(af, typ, 0); err != nil { + time.Sleep(10 * time.Millisecond) + continue + } + break + } + if err != nil { + return nil, fmt.Errorf("%v %d %v", addrFamily(af), typ, err) + } + ms, err := ParseRIB(typ, b) + if err != nil { + return nil, fmt.Errorf("%v %d %v", addrFamily(af), typ, err) + } + return ms, nil +} + +type propVirtual struct { + name string + addr, mask string + setupCmds []*exec.Cmd + teardownCmds []*exec.Cmd +} + +func (ti *propVirtual) setup() error { + for _, cmd := range ti.setupCmds { + if err := cmd.Run(); err != nil { + ti.teardown() + return err + } + } + return nil +} + +func (ti *propVirtual) teardown() error { + for _, cmd := range ti.teardownCmds { + if err := cmd.Run(); err != nil { + return err + } + } + return nil +} + +func (ti *propVirtual) configure(suffix int) error { + if runtime.GOOS == "openbsd" { + ti.name = fmt.Sprintf("vether%d", suffix) + } else { + ti.name = fmt.Sprintf("vlan%d", suffix) + } + xname, err := exec.LookPath("ifconfig") + if err != nil { + return err + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "create"}, + }) + if runtime.GOOS == "netbsd" { + // NetBSD requires an underlying dot1Q-capable network + // interface. + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "vlan", fmt.Sprintf("%d", suffix&0xfff), "vlanif", "wm0"}, + }) + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "inet", ti.addr, "netmask", ti.mask}, + }) + ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "destroy"}, + }) + return nil +} diff --git a/src/vendor/golang.org/x/net/route/sys.go b/src/vendor/golang.org/x/net/route/sys.go new file mode 100644 index 00000000000000..80ca83ae1378fe --- /dev/null +++ b/src/vendor/golang.org/x/net/route/sys.go @@ -0,0 +1,40 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +import "unsafe" + +var ( + nativeEndian binaryByteOrder + kernelAlign int + parseFns map[int]parseFn +) + +func init() { + i := uint32(1) + b := (*[4]byte)(unsafe.Pointer(&i)) + if b[0] == 1 { + nativeEndian = littleEndian + } else { + nativeEndian = bigEndian + } + kernelAlign, parseFns = probeRoutingStack() +} + +func roundup(l int) int { + if l == 0 { + return kernelAlign + } + return (l + kernelAlign - 1) & ^(kernelAlign - 1) +} + +type parseFn func(RIBType, []byte) (Message, error) + +type wireFormat struct { + extOff int // offset of header extension + bodyOff int // offset of message body +} diff --git a/src/vendor/golang.org/x/net/route/sys_darwin.go b/src/vendor/golang.org/x/net/route/sys_darwin.go new file mode 100644 index 00000000000000..fff3a0fd1db29a --- /dev/null +++ b/src/vendor/golang.org/x/net/route/sys_darwin.go @@ -0,0 +1,80 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +func (typ RIBType) parseable() bool { + switch typ { + case sysNET_RT_STAT, sysNET_RT_TRASH: + return false + default: + return true + } +} + +// A RouteMetrics represents route metrics. +type RouteMetrics struct { + PathMTU int // path maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *RouteMessage) Sys() []Sys { + return []Sys{ + &RouteMetrics{ + PathMTU: int(nativeEndian.Uint32(m.raw[m.extOff+4 : m.extOff+8])), + }, + } +} + +// A InterfaceMetrics represents interface metrics. +type InterfaceMetrics struct { + Type int // interface type + MTU int // maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *InterfaceMessage) Sys() []Sys { + return []Sys{ + &InterfaceMetrics{ + Type: int(m.raw[m.extOff]), + MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), + }, + } +} + +func probeRoutingStack() (int, map[int]parseFn) { + rtm := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdrDarwin15} + rtm2 := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdr2Darwin15} + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDarwin15} + ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2Darwin15} + ifam := &wireFormat{extOff: sizeofIfaMsghdrDarwin15, bodyOff: sizeofIfaMsghdrDarwin15} + ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDarwin15, bodyOff: sizeofIfmaMsghdrDarwin15} + ifmam2 := &wireFormat{extOff: sizeofIfmaMsghdr2Darwin15, bodyOff: sizeofIfmaMsghdr2Darwin15} + // Darwin kernels require 32-bit aligned access to routing facilities. + return 4, map[int]parseFn{ + sysRTM_ADD: rtm.parseRouteMessage, + sysRTM_DELETE: rtm.parseRouteMessage, + sysRTM_CHANGE: rtm.parseRouteMessage, + sysRTM_GET: rtm.parseRouteMessage, + sysRTM_LOSING: rtm.parseRouteMessage, + sysRTM_REDIRECT: rtm.parseRouteMessage, + sysRTM_MISS: rtm.parseRouteMessage, + sysRTM_LOCK: rtm.parseRouteMessage, + sysRTM_RESOLVE: rtm.parseRouteMessage, + sysRTM_NEWADDR: ifam.parseInterfaceAddrMessage, + sysRTM_DELADDR: ifam.parseInterfaceAddrMessage, + sysRTM_IFINFO: ifm.parseInterfaceMessage, + sysRTM_NEWMADDR: ifmam.parseInterfaceMulticastAddrMessage, + sysRTM_DELMADDR: ifmam.parseInterfaceMulticastAddrMessage, + sysRTM_IFINFO2: ifm2.parseInterfaceMessage, + sysRTM_NEWMADDR2: ifmam2.parseInterfaceMulticastAddrMessage, + sysRTM_GET2: rtm2.parseRouteMessage, + } +} diff --git a/src/vendor/golang.org/x/net/route/sys_dragonfly.go b/src/vendor/golang.org/x/net/route/sys_dragonfly.go new file mode 100644 index 00000000000000..da848b3d076e5d --- /dev/null +++ b/src/vendor/golang.org/x/net/route/sys_dragonfly.go @@ -0,0 +1,71 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +import "unsafe" + +func (typ RIBType) parseable() bool { return true } + +// A RouteMetrics represents route metrics. +type RouteMetrics struct { + PathMTU int // path maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *RouteMessage) Sys() []Sys { + return []Sys{ + &RouteMetrics{ + PathMTU: int(nativeEndian.Uint64(m.raw[m.extOff+8 : m.extOff+16])), + }, + } +} + +// A InterfaceMetrics represents interface metrics. +type InterfaceMetrics struct { + Type int // interface type + MTU int // maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *InterfaceMessage) Sys() []Sys { + return []Sys{ + &InterfaceMetrics{ + Type: int(m.raw[m.extOff]), + MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), + }, + } +} + +func probeRoutingStack() (int, map[int]parseFn) { + var p uintptr + rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrDragonFlyBSD4} + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDragonFlyBSD4} + ifam := &wireFormat{extOff: sizeofIfaMsghdrDragonFlyBSD4, bodyOff: sizeofIfaMsghdrDragonFlyBSD4} + ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDragonFlyBSD4, bodyOff: sizeofIfmaMsghdrDragonFlyBSD4} + ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4} + return int(unsafe.Sizeof(p)), map[int]parseFn{ + sysRTM_ADD: rtm.parseRouteMessage, + sysRTM_DELETE: rtm.parseRouteMessage, + sysRTM_CHANGE: rtm.parseRouteMessage, + sysRTM_GET: rtm.parseRouteMessage, + sysRTM_LOSING: rtm.parseRouteMessage, + sysRTM_REDIRECT: rtm.parseRouteMessage, + sysRTM_MISS: rtm.parseRouteMessage, + sysRTM_LOCK: rtm.parseRouteMessage, + sysRTM_RESOLVE: rtm.parseRouteMessage, + sysRTM_NEWADDR: ifam.parseInterfaceAddrMessage, + sysRTM_DELADDR: ifam.parseInterfaceAddrMessage, + sysRTM_IFINFO: ifm.parseInterfaceMessage, + sysRTM_NEWMADDR: ifmam.parseInterfaceMulticastAddrMessage, + sysRTM_DELMADDR: ifmam.parseInterfaceMulticastAddrMessage, + sysRTM_IFANNOUNCE: ifanm.parseInterfaceAnnounceMessage, + } +} diff --git a/src/vendor/golang.org/x/net/route/sys_freebsd.go b/src/vendor/golang.org/x/net/route/sys_freebsd.go new file mode 100644 index 00000000000000..7b05c1a5a05c11 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/sys_freebsd.go @@ -0,0 +1,150 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +import ( + "syscall" + "unsafe" +) + +func (typ RIBType) parseable() bool { return true } + +// A RouteMetrics represents route metrics. +type RouteMetrics struct { + PathMTU int // path maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *RouteMessage) Sys() []Sys { + if kernelAlign == 8 { + return []Sys{ + &RouteMetrics{ + PathMTU: int(nativeEndian.Uint64(m.raw[m.extOff+8 : m.extOff+16])), + }, + } + } + return []Sys{ + &RouteMetrics{ + PathMTU: int(nativeEndian.Uint32(m.raw[m.extOff+4 : m.extOff+8])), + }, + } +} + +// A InterfaceMetrics represents interface metrics. +type InterfaceMetrics struct { + Type int // interface type + MTU int // maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *InterfaceMessage) Sys() []Sys { + return []Sys{ + &InterfaceMetrics{ + Type: int(m.raw[m.extOff]), + MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), + }, + } +} + +func probeRoutingStack() (int, map[int]parseFn) { + var p uintptr + wordSize := int(unsafe.Sizeof(p)) + align := int(unsafe.Sizeof(p)) + // In the case of kern.supported_archs="amd64 i386", we need + // to know the underlying kernel's architecture because the + // alignment for routing facilities are set at the build time + // of the kernel. + conf, _ := syscall.Sysctl("kern.conftxt") + for i, j := 0, 0; j < len(conf); j++ { + if conf[j] != '\n' { + continue + } + s := conf[i:j] + i = j + 1 + if len(s) > len("machine") && s[:len("machine")] == "machine" { + s = s[len("machine"):] + for k := 0; k < len(s); k++ { + if s[k] == ' ' || s[k] == '\t' { + s = s[1:] + } + break + } + if s == "amd64" { + align = 8 + } + break + } + } + var rtm, ifm, ifam, ifmam, ifanm *wireFormat + if align != wordSize { // 386 emulation on amd64 + rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10Emu - sizeofRtMetricsFreeBSD10Emu, bodyOff: sizeofRtMsghdrFreeBSD10Emu} + ifm = &wireFormat{extOff: 16} + ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10Emu, bodyOff: sizeofIfaMsghdrFreeBSD10Emu} + ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10Emu, bodyOff: sizeofIfmaMsghdrFreeBSD10Emu} + ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10Emu, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10Emu} + } else { + rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10 - sizeofRtMetricsFreeBSD10, bodyOff: sizeofRtMsghdrFreeBSD10} + ifm = &wireFormat{extOff: 16} + ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10, bodyOff: sizeofIfaMsghdrFreeBSD10} + ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10, bodyOff: sizeofIfmaMsghdrFreeBSD10} + ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10} + } + rel, _ := syscall.SysctlUint32("kern.osreldate") + switch { + case rel < 800000: + if align != wordSize { // 386 emulation on amd64 + ifm.bodyOff = sizeofIfMsghdrFreeBSD7Emu + } else { + ifm.bodyOff = sizeofIfMsghdrFreeBSD7 + } + case 800000 <= rel && rel < 900000: + if align != wordSize { // 386 emulation on amd64 + ifm.bodyOff = sizeofIfMsghdrFreeBSD8Emu + } else { + ifm.bodyOff = sizeofIfMsghdrFreeBSD8 + } + case 900000 <= rel && rel < 1000000: + if align != wordSize { // 386 emulation on amd64 + ifm.bodyOff = sizeofIfMsghdrFreeBSD9Emu + } else { + ifm.bodyOff = sizeofIfMsghdrFreeBSD9 + } + case 1000000 <= rel && rel < 1100000: + if align != wordSize { // 386 emulation on amd64 + ifm.bodyOff = sizeofIfMsghdrFreeBSD10Emu + } else { + ifm.bodyOff = sizeofIfMsghdrFreeBSD10 + } + default: + if align != wordSize { // 386 emulation on amd64 + ifm.bodyOff = sizeofIfMsghdrFreeBSD11Emu + } else { + ifm.bodyOff = sizeofIfMsghdrFreeBSD11 + } + } + return align, map[int]parseFn{ + sysRTM_ADD: rtm.parseRouteMessage, + sysRTM_DELETE: rtm.parseRouteMessage, + sysRTM_CHANGE: rtm.parseRouteMessage, + sysRTM_GET: rtm.parseRouteMessage, + sysRTM_LOSING: rtm.parseRouteMessage, + sysRTM_REDIRECT: rtm.parseRouteMessage, + sysRTM_MISS: rtm.parseRouteMessage, + sysRTM_LOCK: rtm.parseRouteMessage, + sysRTM_RESOLVE: rtm.parseRouteMessage, + sysRTM_NEWADDR: ifam.parseInterfaceAddrMessage, + sysRTM_DELADDR: ifam.parseInterfaceAddrMessage, + sysRTM_IFINFO: ifm.parseInterfaceMessage, + sysRTM_NEWMADDR: ifmam.parseInterfaceMulticastAddrMessage, + sysRTM_DELMADDR: ifmam.parseInterfaceMulticastAddrMessage, + sysRTM_IFANNOUNCE: ifanm.parseInterfaceAnnounceMessage, + } +} diff --git a/src/vendor/golang.org/x/net/route/sys_netbsd.go b/src/vendor/golang.org/x/net/route/sys_netbsd.go new file mode 100644 index 00000000000000..4d8076b518d5b2 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/sys_netbsd.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +func (typ RIBType) parseable() bool { return true } + +// A RouteMetrics represents route metrics. +type RouteMetrics struct { + PathMTU int // path maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *RouteMessage) Sys() []Sys { + return []Sys{ + &RouteMetrics{ + PathMTU: int(nativeEndian.Uint64(m.raw[m.extOff+8 : m.extOff+16])), + }, + } +} + +// A InterfaceMetrics represents interface metrics. +type InterfaceMetrics struct { + Type int // interface type + MTU int // maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *InterfaceMessage) Sys() []Sys { + return []Sys{ + &InterfaceMetrics{ + Type: int(m.raw[m.extOff]), + MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), + }, + } +} + +func probeRoutingStack() (int, map[int]parseFn) { + rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrNetBSD7} + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrNetBSD7} + ifam := &wireFormat{extOff: sizeofIfaMsghdrNetBSD7, bodyOff: sizeofIfaMsghdrNetBSD7} + ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrNetBSD7, bodyOff: sizeofIfAnnouncemsghdrNetBSD7} + // NetBSD 6 and above kernels require 64-bit aligned access to + // routing facilities. + return 8, map[int]parseFn{ + sysRTM_ADD: rtm.parseRouteMessage, + sysRTM_DELETE: rtm.parseRouteMessage, + sysRTM_CHANGE: rtm.parseRouteMessage, + sysRTM_GET: rtm.parseRouteMessage, + sysRTM_LOSING: rtm.parseRouteMessage, + sysRTM_REDIRECT: rtm.parseRouteMessage, + sysRTM_MISS: rtm.parseRouteMessage, + sysRTM_LOCK: rtm.parseRouteMessage, + sysRTM_RESOLVE: rtm.parseRouteMessage, + sysRTM_NEWADDR: ifam.parseInterfaceAddrMessage, + sysRTM_DELADDR: ifam.parseInterfaceAddrMessage, + sysRTM_IFANNOUNCE: ifanm.parseInterfaceAnnounceMessage, + sysRTM_IFINFO: ifm.parseInterfaceMessage, + } +} diff --git a/src/vendor/golang.org/x/net/route/sys_openbsd.go b/src/vendor/golang.org/x/net/route/sys_openbsd.go new file mode 100644 index 00000000000000..26d043869610ea --- /dev/null +++ b/src/vendor/golang.org/x/net/route/sys_openbsd.go @@ -0,0 +1,72 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package route + +import "unsafe" + +func (typ RIBType) parseable() bool { + switch typ { + case sysNET_RT_STATS, sysNET_RT_TABLE: + return false + default: + return true + } +} + +// A RouteMetrics represents route metrics. +type RouteMetrics struct { + PathMTU int // path maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *RouteMessage) Sys() []Sys { + return []Sys{ + &RouteMetrics{ + PathMTU: int(nativeEndian.Uint32(m.raw[60:64])), + }, + } +} + +// A InterfaceMetrics represents interface metrics. +type InterfaceMetrics struct { + Type int // interface type + MTU int // maximum transmission unit +} + +// SysType implements the SysType method of Sys interface. +func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } + +// Sys implements the Sys method of Message interface. +func (m *InterfaceMessage) Sys() []Sys { + return []Sys{ + &InterfaceMetrics{ + Type: int(m.raw[24]), + MTU: int(nativeEndian.Uint32(m.raw[28:32])), + }, + } +} + +func probeRoutingStack() (int, map[int]parseFn) { + var p uintptr + nooff := &wireFormat{extOff: -1, bodyOff: -1} + return int(unsafe.Sizeof(p)), map[int]parseFn{ + sysRTM_ADD: nooff.parseRouteMessage, + sysRTM_DELETE: nooff.parseRouteMessage, + sysRTM_CHANGE: nooff.parseRouteMessage, + sysRTM_GET: nooff.parseRouteMessage, + sysRTM_LOSING: nooff.parseRouteMessage, + sysRTM_REDIRECT: nooff.parseRouteMessage, + sysRTM_MISS: nooff.parseRouteMessage, + sysRTM_LOCK: nooff.parseRouteMessage, + sysRTM_RESOLVE: nooff.parseRouteMessage, + sysRTM_NEWADDR: nooff.parseInterfaceAddrMessage, + sysRTM_DELADDR: nooff.parseInterfaceAddrMessage, + sysRTM_IFINFO: nooff.parseInterfaceMessage, + sysRTM_IFANNOUNCE: nooff.parseInterfaceAnnounceMessage, + } +} diff --git a/src/vendor/golang.org/x/net/route/syscall.go b/src/vendor/golang.org/x/net/route/syscall.go new file mode 100644 index 00000000000000..d136325a30a258 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/syscall.go @@ -0,0 +1,33 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package route + +import ( + "syscall" + "unsafe" +) + +// TODO: replace with runtime.KeepAlive when available +//go:noescape +func keepAlive(p unsafe.Pointer) + +var zero uintptr + +func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + var p unsafe.Pointer + if len(mib) > 0 { + p = unsafe.Pointer(&mib[0]) + } else { + p = unsafe.Pointer(&zero) + } + _, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(p), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + keepAlive(p) + if errno != 0 { + return error(errno) + } + return nil +} diff --git a/src/vendor/golang.org/x/net/route/syscall.s b/src/vendor/golang.org/x/net/route/syscall.s new file mode 100644 index 00000000000000..fa6297f0aa127d --- /dev/null +++ b/src/vendor/golang.org/x/net/route/syscall.s @@ -0,0 +1,8 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·keepAlive(SB),NOSPLIT,$0 + RET diff --git a/src/vendor/golang.org/x/net/route/zsys_darwin.go b/src/vendor/golang.org/x/net/route/zsys_darwin.go new file mode 100644 index 00000000000000..265b81cd50e695 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_darwin.go @@ -0,0 +1,93 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_darwin.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x11 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x1e + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x3 + sysNET_RT_STAT = 0x4 + sysNET_RT_TRASH = 0x5 + sysNET_RT_IFLIST2 = 0x6 + sysNET_RT_DUMP2 = 0x7 + sysNET_RT_MAXID = 0xa +) + +const ( + sysCTL_MAXNAME = 0xc + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_VFS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_USER = 0x8 + sysCTL_MAXID = 0x9 +) + +const ( + sysRTM_VERSION = 0x5 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_OLDADD = 0x9 + sysRTM_OLDDEL = 0xa + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFINFO = 0xe + sysRTM_NEWMADDR = 0xf + sysRTM_DELMADDR = 0x10 + sysRTM_IFINFO2 = 0x12 + sysRTM_NEWMADDR2 = 0x13 + sysRTM_GET2 = 0x14 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_MAX = 0x8 +) + +const ( + sizeofIfMsghdrDarwin15 = 0x70 + sizeofIfaMsghdrDarwin15 = 0x14 + sizeofIfmaMsghdrDarwin15 = 0x10 + sizeofIfMsghdr2Darwin15 = 0xa0 + sizeofIfmaMsghdr2Darwin15 = 0x14 + sizeofIfDataDarwin15 = 0x60 + sizeofIfData64Darwin15 = 0x80 + + sizeofRtMsghdrDarwin15 = 0x5c + sizeofRtMsghdr2Darwin15 = 0x5c + sizeofRtMetricsDarwin15 = 0x38 +) diff --git a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go new file mode 100644 index 00000000000000..dd36dece0f6dbf --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go @@ -0,0 +1,92 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_dragonfly.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x11 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x1c + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x3 + sysNET_RT_MAXID = 0x4 +) + +const ( + sysCTL_MAXNAME = 0xc + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_VFS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_USER = 0x8 + sysCTL_P1003_1B = 0x9 + sysCTL_LWKT = 0xa + sysCTL_MAXID = 0xb +) + +const ( + sysRTM_VERSION = 0x6 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_OLDADD = 0x9 + sysRTM_OLDDEL = 0xa + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFINFO = 0xe + sysRTM_NEWMADDR = 0xf + sysRTM_DELMADDR = 0x10 + sysRTM_IFANNOUNCE = 0x11 + sysRTM_IEEE80211 = 0x12 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + sysRTA_MPLS1 = 0x100 + sysRTA_MPLS2 = 0x200 + sysRTA_MPLS3 = 0x400 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_MPLS1 = 0x8 + sysRTAX_MPLS2 = 0x9 + sysRTAX_MPLS3 = 0xa + sysRTAX_MAX = 0xb +) + +const ( + sizeofIfMsghdrDragonFlyBSD4 = 0xb0 + sizeofIfaMsghdrDragonFlyBSD4 = 0x14 + sizeofIfmaMsghdrDragonFlyBSD4 = 0x10 + sizeofIfAnnouncemsghdrDragonFlyBSD4 = 0x18 + + sizeofRtMsghdrDragonFlyBSD4 = 0x98 + sizeofRtMetricsDragonFlyBSD4 = 0x70 +) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go new file mode 100644 index 00000000000000..9bac2e3900fbdb --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go @@ -0,0 +1,120 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x11 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x1c + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x3 + sysNET_RT_IFMALIST = 0x4 + sysNET_RT_IFLISTL = 0x5 +) + +const ( + sysCTL_MAXNAME = 0x18 + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_VFS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_USER = 0x8 + sysCTL_P1003_1B = 0x9 +) + +const ( + sysRTM_VERSION = 0x5 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFINFO = 0xe + sysRTM_NEWMADDR = 0xf + sysRTM_DELMADDR = 0x10 + sysRTM_IFANNOUNCE = 0x11 + sysRTM_IEEE80211 = 0x12 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_MAX = 0x8 +) + +const ( + sizeofIfMsghdrlFreeBSD10 = 0x68 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0x6c + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + + sizeofRtMsghdrFreeBSD10 = 0x5c + sizeofRtMetricsFreeBSD10 = 0x38 + + sizeofIfMsghdrFreeBSD7 = 0x60 + sizeofIfMsghdrFreeBSD8 = 0x60 + sizeofIfMsghdrFreeBSD9 = 0x60 + sizeofIfMsghdrFreeBSD10 = 0x64 + sizeofIfMsghdrFreeBSD11 = 0xa8 + + sizeofIfDataFreeBSD7 = 0x50 + sizeofIfDataFreeBSD8 = 0x50 + sizeofIfDataFreeBSD9 = 0x50 + sizeofIfDataFreeBSD10 = 0x54 + sizeofIfDataFreeBSD11 = 0x98 + + // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 + // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT + + sizeofIfMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + + sizeofRtMsghdrFreeBSD10Emu = 0x98 + sizeofRtMetricsFreeBSD10Emu = 0x70 + + sizeofIfMsghdrFreeBSD7Emu = 0xa8 + sizeofIfMsghdrFreeBSD8Emu = 0xa8 + sizeofIfMsghdrFreeBSD9Emu = 0xa8 + sizeofIfMsghdrFreeBSD10Emu = 0xa8 + sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x98 + sizeofIfDataFreeBSD8Emu = 0x98 + sizeofIfDataFreeBSD9Emu = 0x98 + sizeofIfDataFreeBSD10Emu = 0x98 + sizeofIfDataFreeBSD11Emu = 0x98 +) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go new file mode 100644 index 00000000000000..b1920d7ac16be8 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go @@ -0,0 +1,117 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x11 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x1c + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x3 + sysNET_RT_IFMALIST = 0x4 + sysNET_RT_IFLISTL = 0x5 +) + +const ( + sysCTL_MAXNAME = 0x18 + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_VFS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_USER = 0x8 + sysCTL_P1003_1B = 0x9 +) + +const ( + sysRTM_VERSION = 0x5 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFINFO = 0xe + sysRTM_NEWMADDR = 0xf + sysRTM_DELMADDR = 0x10 + sysRTM_IFANNOUNCE = 0x11 + sysRTM_IEEE80211 = 0x12 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_MAX = 0x8 +) + +const ( + sizeofIfMsghdrlFreeBSD10 = 0xb0 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0xb0 + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + + sizeofRtMsghdrFreeBSD10 = 0x98 + sizeofRtMetricsFreeBSD10 = 0x70 + + sizeofIfMsghdrFreeBSD7 = 0xa8 + sizeofIfMsghdrFreeBSD8 = 0xa8 + sizeofIfMsghdrFreeBSD9 = 0xa8 + sizeofIfMsghdrFreeBSD10 = 0xa8 + sizeofIfMsghdrFreeBSD11 = 0xa8 + + sizeofIfDataFreeBSD7 = 0x98 + sizeofIfDataFreeBSD8 = 0x98 + sizeofIfDataFreeBSD9 = 0x98 + sizeofIfDataFreeBSD10 = 0x98 + sizeofIfDataFreeBSD11 = 0x98 + + sizeofIfMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + + sizeofRtMsghdrFreeBSD10Emu = 0x98 + sizeofRtMetricsFreeBSD10Emu = 0x70 + + sizeofIfMsghdrFreeBSD7Emu = 0xa8 + sizeofIfMsghdrFreeBSD8Emu = 0xa8 + sizeofIfMsghdrFreeBSD9Emu = 0xa8 + sizeofIfMsghdrFreeBSD10Emu = 0xa8 + sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x98 + sizeofIfDataFreeBSD8Emu = 0x98 + sizeofIfDataFreeBSD9Emu = 0x98 + sizeofIfDataFreeBSD10Emu = 0x98 + sizeofIfDataFreeBSD11Emu = 0x98 +) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go new file mode 100644 index 00000000000000..a034d6fcbf215c --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go @@ -0,0 +1,117 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x11 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x1c + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x3 + sysNET_RT_IFMALIST = 0x4 + sysNET_RT_IFLISTL = 0x5 +) + +const ( + sysCTL_MAXNAME = 0x18 + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_VFS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_USER = 0x8 + sysCTL_P1003_1B = 0x9 +) + +const ( + sysRTM_VERSION = 0x5 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFINFO = 0xe + sysRTM_NEWMADDR = 0xf + sysRTM_DELMADDR = 0x10 + sysRTM_IFANNOUNCE = 0x11 + sysRTM_IEEE80211 = 0x12 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_MAX = 0x8 +) + +const ( + sizeofIfMsghdrlFreeBSD10 = 0x68 + sizeofIfaMsghdrFreeBSD10 = 0x14 + sizeofIfaMsghdrlFreeBSD10 = 0x6c + sizeofIfmaMsghdrFreeBSD10 = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 + + sizeofRtMsghdrFreeBSD10 = 0x5c + sizeofRtMetricsFreeBSD10 = 0x38 + + sizeofIfMsghdrFreeBSD7 = 0x70 + sizeofIfMsghdrFreeBSD8 = 0x70 + sizeofIfMsghdrFreeBSD9 = 0x70 + sizeofIfMsghdrFreeBSD10 = 0x70 + sizeofIfMsghdrFreeBSD11 = 0xa8 + + sizeofIfDataFreeBSD7 = 0x60 + sizeofIfDataFreeBSD8 = 0x60 + sizeofIfDataFreeBSD9 = 0x60 + sizeofIfDataFreeBSD10 = 0x60 + sizeofIfDataFreeBSD11 = 0x98 + + sizeofIfMsghdrlFreeBSD10Emu = 0x68 + sizeofIfaMsghdrFreeBSD10Emu = 0x14 + sizeofIfaMsghdrlFreeBSD10Emu = 0x6c + sizeofIfmaMsghdrFreeBSD10Emu = 0x10 + sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 + + sizeofRtMsghdrFreeBSD10Emu = 0x5c + sizeofRtMetricsFreeBSD10Emu = 0x38 + + sizeofIfMsghdrFreeBSD7Emu = 0x70 + sizeofIfMsghdrFreeBSD8Emu = 0x70 + sizeofIfMsghdrFreeBSD9Emu = 0x70 + sizeofIfMsghdrFreeBSD10Emu = 0x70 + sizeofIfMsghdrFreeBSD11Emu = 0xa8 + + sizeofIfDataFreeBSD7Emu = 0x60 + sizeofIfDataFreeBSD8Emu = 0x60 + sizeofIfDataFreeBSD9Emu = 0x60 + sizeofIfDataFreeBSD10Emu = 0x60 + sizeofIfDataFreeBSD11Emu = 0x98 +) diff --git a/src/vendor/golang.org/x/net/route/zsys_netbsd.go b/src/vendor/golang.org/x/net/route/zsys_netbsd.go new file mode 100644 index 00000000000000..aa4aad1613e22f --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_netbsd.go @@ -0,0 +1,91 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_netbsd.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x22 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x18 + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x5 + sysNET_RT_MAXID = 0x6 +) + +const ( + sysCTL_MAXNAME = 0xc + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_VFS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_USER = 0x8 + sysCTL_DDB = 0x9 + sysCTL_PROC = 0xa + sysCTL_VENDOR = 0xb + sysCTL_EMUL = 0xc + sysCTL_SECURITY = 0xd + sysCTL_MAXID = 0xe +) + +const ( + sysRTM_VERSION = 0x4 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_OLDADD = 0x9 + sysRTM_OLDDEL = 0xa + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFANNOUNCE = 0x10 + sysRTM_IEEE80211 = 0x11 + sysRTM_SETGATE = 0x12 + sysRTM_LLINFO_UPD = 0x13 + sysRTM_IFINFO = 0x14 + sysRTM_CHGADDR = 0x15 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + sysRTA_TAG = 0x100 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_TAG = 0x8 + sysRTAX_MAX = 0x9 +) + +const ( + sizeofIfMsghdrNetBSD7 = 0x98 + sizeofIfaMsghdrNetBSD7 = 0x18 + sizeofIfAnnouncemsghdrNetBSD7 = 0x18 + + sizeofRtMsghdrNetBSD7 = 0x78 + sizeofRtMetricsNetBSD7 = 0x50 +) diff --git a/src/vendor/golang.org/x/net/route/zsys_openbsd.go b/src/vendor/golang.org/x/net/route/zsys_openbsd.go new file mode 100644 index 00000000000000..4fadc4e8fa1e54 --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_openbsd.go @@ -0,0 +1,80 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_openbsd.go + +package route + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_ROUTE = 0x11 + sysAF_LINK = 0x12 + sysAF_INET6 = 0x18 + + sysNET_RT_DUMP = 0x1 + sysNET_RT_FLAGS = 0x2 + sysNET_RT_IFLIST = 0x3 + sysNET_RT_STATS = 0x4 + sysNET_RT_TABLE = 0x5 + sysNET_RT_IFNAMES = 0x6 + sysNET_RT_MAXID = 0x7 +) + +const ( + sysCTL_MAXNAME = 0xc + + sysCTL_UNSPEC = 0x0 + sysCTL_KERN = 0x1 + sysCTL_VM = 0x2 + sysCTL_FS = 0x3 + sysCTL_NET = 0x4 + sysCTL_DEBUG = 0x5 + sysCTL_HW = 0x6 + sysCTL_MACHDEP = 0x7 + sysCTL_DDB = 0x9 + sysCTL_VFS = 0xa + sysCTL_MAXID = 0xb +) + +const ( + sysRTM_VERSION = 0x5 + + sysRTM_ADD = 0x1 + sysRTM_DELETE = 0x2 + sysRTM_CHANGE = 0x3 + sysRTM_GET = 0x4 + sysRTM_LOSING = 0x5 + sysRTM_REDIRECT = 0x6 + sysRTM_MISS = 0x7 + sysRTM_LOCK = 0x8 + sysRTM_RESOLVE = 0xb + sysRTM_NEWADDR = 0xc + sysRTM_DELADDR = 0xd + sysRTM_IFINFO = 0xe + sysRTM_IFANNOUNCE = 0xf + sysRTM_DESYNC = 0x10 + + sysRTA_DST = 0x1 + sysRTA_GATEWAY = 0x2 + sysRTA_NETMASK = 0x4 + sysRTA_GENMASK = 0x8 + sysRTA_IFP = 0x10 + sysRTA_IFA = 0x20 + sysRTA_AUTHOR = 0x40 + sysRTA_BRD = 0x80 + sysRTA_SRC = 0x100 + sysRTA_SRCMASK = 0x200 + sysRTA_LABEL = 0x400 + + sysRTAX_DST = 0x0 + sysRTAX_GATEWAY = 0x1 + sysRTAX_NETMASK = 0x2 + sysRTAX_GENMASK = 0x3 + sysRTAX_IFP = 0x4 + sysRTAX_IFA = 0x5 + sysRTAX_AUTHOR = 0x6 + sysRTAX_BRD = 0x7 + sysRTAX_SRC = 0x8 + sysRTAX_SRCMASK = 0x9 + sysRTAX_LABEL = 0xa + sysRTAX_MAX = 0xb +) diff --git a/test/fixedbugs/bug398.go b/test/fixedbugs/bug398.go index a80a960394470b..81bf33c37a6df1 100644 --- a/test/fixedbugs/bug398.go +++ b/test/fixedbugs/bug398.go @@ -8,17 +8,20 @@ package p -type I1 interface { - F() interface{I1} +type i1 interface { + F() interface{i1} } -type I2 interface { - F() interface{I2} +type i2 interface { + F() interface{i2} } -var v1 I1 -var v2 I2 +var v1 i1 +var v2 i2 func f() bool { return v1 == v2 } + +// TODO(gri) Change test to use exported interfaces. +// See issue #15596 for details. \ No newline at end of file diff --git a/test/fixedbugs/issue13779.go b/test/fixedbugs/issue13779.go index 94cf9c68de0fe9..b18577c1528496 100644 --- a/test/fixedbugs/issue13779.go +++ b/test/fixedbugs/issue13779.go @@ -11,5 +11,5 @@ package main func main() { type person struct{ age, weight, height int } students := map[string]person{"sally": person{12, 50, 32}} - students["sally"].age = 3 // ERROR "cannot directly assign to struct field .* in map" + students["sally"].age = 3 // ERROR "cannot assign to struct field .* in map" } diff --git a/test/fixedbugs/issue14136.go b/test/fixedbugs/issue14136.go new file mode 100644 index 00000000000000..928a60bf6b5ab8 --- /dev/null +++ b/test/fixedbugs/issue14136.go @@ -0,0 +1,19 @@ +// errorcheck + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that > 10 non-syntax errors on the same line +// don't lead to early exit. Specifically, here test +// that we see the initialization error for variable +// s. + +package main + +type T struct{} + +func main() { + t := T{X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1} // ERROR "unknown T field" + var s string = 1 // ERROR "cannot use 1" +} diff --git a/test/fixedbugs/issue15277.go b/test/fixedbugs/issue15277.go new file mode 100644 index 00000000000000..719c9a4f4a7aae --- /dev/null +++ b/test/fixedbugs/issue15277.go @@ -0,0 +1,38 @@ +// run + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +build amd64 + +package main + +import "runtime" + +type big [10 << 20]byte + +func f(x *big, start int64) { + if delta := inuse() - start; delta < 9<<20 { + println("after alloc: expected delta at least 9MB, got: ", delta) + } + x = nil + if delta := inuse() - start; delta > 1<<20 { + println("after drop: expected delta below 1MB, got: ", delta) + } + x = new(big) + if delta := inuse() - start; delta < 9<<20 { + println("second alloc: expected delta at least 9MB, got: ", delta) + } +} + +func main() { + x := inuse() + f(new(big), x) +} + +func inuse() int64 { + runtime.GC() + var st runtime.MemStats + runtime.ReadMemStats(&st) + return int64(st.Alloc) +} diff --git a/test/fixedbugs/issue15329.go b/test/fixedbugs/issue15329.go new file mode 100644 index 00000000000000..30fbf137970651 --- /dev/null +++ b/test/fixedbugs/issue15329.go @@ -0,0 +1,79 @@ +// run + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Previously, cmd/compile would rewrite +// +// check(unsafe.Pointer(testMeth(1).Pointer()), unsafe.Pointer(testMeth(2).Pointer())) +// +// to +// +// var autotmp_1 uintptr = testMeth(1).Pointer() +// var autotmp_2 uintptr = testMeth(2).Pointer() +// check(unsafe.Pointer(autotmp_1), unsafe.Pointer(autotmp_2)) +// +// However, that means autotmp_1 is the only reference to the int +// variable containing the value "1", but it's not a pointer type, +// so it was at risk of being garbage collected by the evaluation of +// testMeth(2).Pointer(), even though package unsafe's documentation +// says the original code was allowed. +// +// Now cmd/compile rewrites it to +// +// var autotmp_1 unsafe.Pointer = unsafe.Pointer(testMeth(1).Pointer()) +// var autotmp_2 unsafe.Pointer = unsafe.Pointer(testMeth(2).Pointer()) +// check(autotmp_1, autotmp_2) +// +// to ensure the pointed-to variables are visible to the GC. + +package main + +import ( + "fmt" + "reflect" + "runtime" + "unsafe" +) + +func main() { + // Test all the different ways we can invoke reflect.Value.Pointer. + + // Direct method invocation. + check(unsafe.Pointer(testMeth(1).Pointer()), unsafe.Pointer(testMeth(2).Pointer())) + + // Invocation via method expression. + check(unsafe.Pointer(reflect.Value.Pointer(testMeth(1))), unsafe.Pointer(reflect.Value.Pointer(testMeth(2)))) + + // Invocation via interface. + check(unsafe.Pointer(testInter(1).Pointer()), unsafe.Pointer(testInter(2).Pointer())) + + // Invocation via method value. + check(unsafe.Pointer(testFunc(1)()), unsafe.Pointer(testFunc(2)())) +} + +func check(p, q unsafe.Pointer) { + a, b := *(*int)(p), *(*int)(q) + if a != 1 || b != 2 { + fmt.Printf("got %v, %v; expected 1, 2\n", a, b) + } +} + +func testMeth(x int) reflect.Value { + // Force GC to run. + runtime.GC() + return reflect.ValueOf(&x) +} + +type Pointerer interface { + Pointer() uintptr +} + +func testInter(x int) Pointerer { + return testMeth(x) +} + +func testFunc(x int) func() uintptr { + return testMeth(x).Pointer +} diff --git a/test/fixedbugs/issue15548.dir/c.go b/test/fixedbugs/issue15548.dir/c.go index ce6e3204b3ea2c..6d3f3be53ec2a4 100644 --- a/test/fixedbugs/issue15548.dir/c.go +++ b/test/fixedbugs/issue15548.dir/c.go @@ -5,6 +5,6 @@ package c import ( - _ "./a" _ "./b" + _ "./a" ) diff --git a/test/fixedbugs/issue15572.dir/a.go b/test/fixedbugs/issue15572.dir/a.go new file mode 100644 index 00000000000000..13566014304714 --- /dev/null +++ b/test/fixedbugs/issue15572.dir/a.go @@ -0,0 +1,40 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type T struct { +} + +func F() []T { + return []T{T{}} +} + +func Fi() []T { + return []T{{}} // element with implicit composite literal type +} + +func Fp() []*T { + return []*T{&T{}} +} + +func Fip() []*T { + return []*T{{}} // element with implicit composite literal type +} + +func Gp() map[int]*T { + return map[int]*T{0: &T{}} +} + +func Gip() map[int]*T { + return map[int]*T{0: {}} // element with implicit composite literal type +} + +func Hp() map[*T]int { + return map[*T]int{&T{}: 0} +} + +func Hip() map[*T]int { + return map[*T]int{{}: 0} // key with implicit composite literal type +} diff --git a/test/fixedbugs/issue15572.dir/b.go b/test/fixedbugs/issue15572.dir/b.go new file mode 100644 index 00000000000000..355accc88057bd --- /dev/null +++ b/test/fixedbugs/issue15572.dir/b.go @@ -0,0 +1,27 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import "./a" + +func F() { + a.F() + a.Fi() +} + +func Fp() { + a.Fp() + a.Fip() +} + +func Gp() { + a.Gp() + a.Gip() +} + +func Hp() { + a.Hp() + a.Hip() +} diff --git a/test/fixedbugs/issue15572.go b/test/fixedbugs/issue15572.go new file mode 100644 index 00000000000000..cf77778f6640ff --- /dev/null +++ b/test/fixedbugs/issue15572.go @@ -0,0 +1,11 @@ +// compiledir + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that exporting composite literals with implicit +// types doesn't crash the typechecker when running over +// inlined function bodies containing such literals. + +package ignored diff --git a/test/fixedbugs/issue15585.go b/test/fixedbugs/issue15585.go new file mode 100644 index 00000000000000..79eb13f90db45f --- /dev/null +++ b/test/fixedbugs/issue15585.go @@ -0,0 +1,45 @@ +// compile + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bug + +func example(n int) (rc int) { + var cc, ll, pp, rr [27]int + for q0 := 0; q0 < n-2; q0++ { + for q1 := q0 + 2; q1 < n; q1++ { + var c, d, l, p, r int + b0 := 1 << uint(q0) + b1 := 1 << uint(q1) + l = ((b0 << 1) | b1) << 1 + c = b0 | b1 | (-1 << uint(n)) + r = ((b0 >> 1) | b1) >> 1 + E: + if c != -1 { + p = ^(l | c | r) + } else { + rc++ + goto R + } + L: + if p != 0 { + lsb := p & -p + p &^= lsb + ll[d], cc[d], rr[d], pp[d] = l, c, r, p + l, c, r = (l|lsb)<<1, c|lsb, (r|lsb)>>1 + d++ + goto E + } + R: + d-- + if d >= 0 { + l, c, r, p = ll[d], cc[d], rr[d], pp[d] + goto L + } + } + } + rc <<= 1 + return +} diff --git a/test/fixedbugs/issue15602.go b/test/fixedbugs/issue15602.go new file mode 100644 index 00000000000000..badf8133c52176 --- /dev/null +++ b/test/fixedbugs/issue15602.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func f(i interface{}) { + i, _ = i.(error) +} diff --git a/test/fixedbugs/issue15604.go b/test/fixedbugs/issue15604.go new file mode 100644 index 00000000000000..4dc0b0b0541308 --- /dev/null +++ b/test/fixedbugs/issue15604.go @@ -0,0 +1,17 @@ +// compile + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bug + +import "os" + +func f(err error) { + var ok bool + if err, ok = err.(*os.PathError); ok { + if err == os.ErrNotExist { + } + } +} diff --git a/test/fixedbugs/issue15646.dir/a.go b/test/fixedbugs/issue15646.dir/a.go new file mode 100644 index 00000000000000..842f19685fd282 --- /dev/null +++ b/test/fixedbugs/issue15646.dir/a.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type T struct{} + +func (T) m() string { + return "m" +} + +func (*T) mp() string { + return "mp" +} + +func F() func(T) string { + return T.m // method expression +} + +func Fp() func(*T) string { + return (*T).mp // method expression +} diff --git a/test/fixedbugs/issue15646.dir/b.go b/test/fixedbugs/issue15646.dir/b.go new file mode 100644 index 00000000000000..3d011ba30195b8 --- /dev/null +++ b/test/fixedbugs/issue15646.dir/b.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" // import must succeed + +func main() { + if a.F()(a.T{}) != "m" { + panic(0) + } + if a.Fp()(nil) != "mp" { + panic(1) + } +} diff --git a/test/fixedbugs/issue15646.go b/test/fixedbugs/issue15646.go new file mode 100644 index 00000000000000..cd4ba9d4e52035 --- /dev/null +++ b/test/fixedbugs/issue15646.go @@ -0,0 +1,9 @@ +// rundir + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that method expressions are correctly encoded +// in binary export data and can be imported again. +package ignore \ No newline at end of file diff --git a/test/fixedbugs/issue15733.go b/test/fixedbugs/issue15733.go new file mode 100644 index 00000000000000..8f609e634dd817 --- /dev/null +++ b/test/fixedbugs/issue15733.go @@ -0,0 +1,23 @@ +// compile + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type S struct { + a [1 << 16]byte +} + +func f1() { + p := &S{} + _ = p +} + +type T [1 << 16]byte + +func f2() { + p := &T{} + _ = p +} diff --git a/test/fixedbugs/issue15747.go b/test/fixedbugs/issue15747.go new file mode 100644 index 00000000000000..34ec719f1281e2 --- /dev/null +++ b/test/fixedbugs/issue15747.go @@ -0,0 +1,41 @@ +// errorcheck -0 -live + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 15747: liveness analysis was marking heap-escaped params live too much, +// and worse was using the wrong bitmap bits to do so. + +package p + +var global *[]byte + +type Q struct{} + +type T struct{ M string } + +var b bool + +func f1(q *Q, xx []byte) interface{} { // ERROR "live at entry to f1: q xx" "live at call to newobject: q xx" "live at call to writebarrierptr: q &xx" + // xx was copied from the stack to the heap on the previous line: + // xx was live for the first two prints but then it switched to &xx + // being live. We should not see plain xx again. + if b { + global = &xx // ERROR "live at call to writebarrierptr: q &xx$" + } + xx, _, err := f2(xx, 5) // ERROR "live at call to newobject: q( d)? &xx( odata.ptr)?" "live at call to writebarrierptr: q (e|err.data err.type)$" + if err != nil { + return err + } + return nil +} + +func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d" + if n > len(d) { + return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d" + } + res = d[:n] + odata = d[n:] + return +} diff --git a/test/fixedbugs/issue15747b.go b/test/fixedbugs/issue15747b.go new file mode 100644 index 00000000000000..9620d3d0cb21a8 --- /dev/null +++ b/test/fixedbugs/issue15747b.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 15747: If a ODCL is dropped, for example when inlining, +// then it's easy to end up not initializing the '&x' pseudo-variable +// to point to an actual allocation. The liveness analysis will detect +// this and abort the computation, so this test just checks that the +// compilation succeeds. + +package p + +type R [100]byte + +func (x R) New() *R { + return &x +} diff --git a/test/fixedbugs/issue15838.dir/a.go b/test/fixedbugs/issue15838.dir/a.go new file mode 100644 index 00000000000000..15b7f1dcfa8230 --- /dev/null +++ b/test/fixedbugs/issue15838.dir/a.go @@ -0,0 +1,61 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +func F1() { +L: + goto L +} + +func F2() { +L: + for { + break L + } +} + +func F3() { +L: + for { + continue L + } +} + +func F4() { + switch { + case true: + fallthrough + default: + } +} + +type T struct{} + +func (T) M1() { +L: + goto L +} + +func (T) M2() { +L: + for { + break L + } +} + +func (T) M3() { +L: + for { + continue L + } +} + +func (T) M4() { + switch { + case true: + fallthrough + default: + } +} diff --git a/test/fixedbugs/issue15838.dir/b.go b/test/fixedbugs/issue15838.dir/b.go new file mode 100644 index 00000000000000..9fd6efc33c9b89 --- /dev/null +++ b/test/fixedbugs/issue15838.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import "./a" + +type T struct{ a.T } diff --git a/test/fixedbugs/issue15838.go b/test/fixedbugs/issue15838.go new file mode 100644 index 00000000000000..fb1c64d1ac19dc --- /dev/null +++ b/test/fixedbugs/issue15838.go @@ -0,0 +1,12 @@ +// compiledir + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test cases for issue #15838, and related failures. +// Make sure the importer correctly sets up nodes for +// label decls, goto, continue, break, and fallthrough +// statements. + +package ignored diff --git a/test/linkobj.go b/test/linkobj.go new file mode 100644 index 00000000000000..8a86aa872f0362 --- /dev/null +++ b/test/linkobj.go @@ -0,0 +1,155 @@ +// +build !nacl +// run + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test the compiler -linkobj flag. + +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "strings" +) + +var pwd, tmpdir string + +func main() { + dir, err := ioutil.TempDir("", "go-test-linkobj-") + if err != nil { + log.Fatal(err) + } + pwd, err = os.Getwd() + if err != nil { + log.Fatal(err) + } + if err := os.Chdir(dir); err != nil { + os.RemoveAll(dir) + log.Fatal(err) + } + tmpdir = dir + + writeFile("p1.go", ` + package p1 + + func F() { + println("hello from p1") + } + `) + writeFile("p2.go", ` + package p2 + + import "./p1" + + func F() { + p1.F() + println("hello from p2") + } + + func main() {} + `) + writeFile("p3.go", ` + package main + + import "./p2" + + func main() { + p2.F() + println("hello from main") + } + `) + + // two rounds: once using normal objects, again using .a files (compile -pack). + for round := 0; round < 2; round++ { + pkg := "-pack=" + fmt.Sprint(round) + + // The compiler expects the files being read to have the right suffix. + o := "o" + if round == 1 { + o = "a" + } + + // inlining is disabled to make sure that the link objects contain needed code. + run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p1."+o, "-linkobj", "p1.lo", "p1.go") + run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p2."+o, "-linkobj", "p2.lo", "p2.go") + run("go", "tool", "compile", pkg, "-D", ".", "-I", ".", "-l", "-o", "p3."+o, "-linkobj", "p3.lo", "p3.go") + + cp("p1."+o, "p1.oo") + cp("p2."+o, "p2.oo") + cp("p3."+o, "p3.oo") + cp("p1.lo", "p1."+o) + cp("p2.lo", "p2."+o) + cp("p3.lo", "p3."+o) + out := runFail("go", "tool", "link", "p2."+o) + if !strings.Contains(out, "not package main") { + fatalf("link p2.o failed but not for package main:\n%s", out) + } + + run("go", "tool", "link", "-L", ".", "-o", "a.out.exe", "p3."+o) + out = run("./a.out.exe") + if !strings.Contains(out, "hello from p1\nhello from p2\nhello from main\n") { + fatalf("running main, incorrect output:\n%s", out) + } + + // ensure that mistaken future round can't use these + os.Remove("p1.o") + os.Remove("a.out.exe") + } + + cleanup() +} + +func run(args ...string) string { + out, err := exec.Command(args[0], args[1:]...).CombinedOutput() + if err != nil { + fatalf("run %v: %s\n%s", args, err, out) + } + return string(out) +} + +func runFail(args ...string) string { + out, err := exec.Command(args[0], args[1:]...).CombinedOutput() + if err == nil { + fatalf("runFail %v: unexpected success!\n%s", args, err, out) + } + return string(out) +} + +func cp(src, dst string) { + data, err := ioutil.ReadFile(src) + if err != nil { + fatalf("%v", err) + } + err = ioutil.WriteFile(dst, data, 0666) + if err != nil { + fatalf("%v", err) + } +} + +func writeFile(name, data string) { + err := ioutil.WriteFile(name, []byte(data), 0666) + if err != nil { + fatalf("%v", err) + } +} + +func cleanup() { + const debug = false + if debug { + println("TMPDIR:", tmpdir) + return + } + os.Chdir(pwd) // get out of tmpdir before removing it + os.RemoveAll(tmpdir) +} + +func fatalf(format string, args ...interface{}) { + cleanup() + log.Fatalf(format, args...) +}